diff --git a/Sources/NIO/ByteBuffer-int.swift b/Sources/NIO/ByteBuffer-int.swift index 71ca4b0b..865cf3a5 100644 --- a/Sources/NIO/ByteBuffer-int.swift +++ b/Sources/NIO/ByteBuffer-int.swift @@ -112,7 +112,9 @@ extension FixedWidthInteger { } extension UInt32 { - /// Returns the next power of two unless that would overflow in which case UInt32.max is returned. + /// Returns the next power of two unless that would overflow, in which case UInt32.max (on 64-bit systems) or + /// Int32.max (on 32-bit systems) is returned. The returned value is always safe to be cast to Int and passed + /// to malloc on all platforms. public func nextPowerOf2ClampedToMax() -> UInt32 { guard self > 0 else { return 1 @@ -120,13 +122,19 @@ extension UInt32 { var n = self + #if arch(arm) // 32-bit, Raspi/AppleWatch/etc + let max = UInt32(Int32.max) + #else + let max = UInt32.max + #endif + n -= 1 n |= n >> 1 n |= n >> 2 n |= n >> 4 n |= n >> 8 n |= n >> 16 - if n != .max { + if n != max { n += 1 } diff --git a/Sources/NIO/Socket.swift b/Sources/NIO/Socket.swift index 109cf21f..25d8c53e 100644 --- a/Sources/NIO/Socket.swift +++ b/Sources/NIO/Socket.swift @@ -20,7 +20,14 @@ public typealias IOVector = iovec /// The maximum number of bytes to write per `writev` call. static var writevLimitBytes: Int { - return Int(Int32.max) + #if arch(arm) // 32-bit, Raspi/AppleWatch/etc + // Note(hh): This is not a _proper_ fix, but necessary because + // other places extend on that. Should be fine in + // practice on 32-bit platforms. + return Int(Int32.max / 4) + #else + return Int(Int32.max) + #endif } /// The maximum number of `IOVector`s to write per `writev` call. diff --git a/Sources/NIOWebSocket/WebSocketFrameEncoder.swift b/Sources/NIOWebSocket/WebSocketFrameEncoder.swift index 1740557a..c7e4f092 100644 --- a/Sources/NIOWebSocket/WebSocketFrameEncoder.swift +++ b/Sources/NIOWebSocket/WebSocketFrameEncoder.swift @@ -16,7 +16,14 @@ import NIO private let maxOneByteSize = 125 private let maxTwoByteSize = Int(UInt16.max) -private let maxNIOFrameSize = Int(UInt32.max) +#if arch(arm) // 32-bit, Raspi/AppleWatch/etc + // Note(hh): This is not a _proper_ fix, but necessary because + // other places extend on that. Should be fine in + // practice on 32-bit platforms. + private let maxNIOFrameSize = Int(Int32.max / 4) +#else + private let maxNIOFrameSize = Int(UInt32.max) +#endif /// An inbound `ChannelHandler` that serializes structured websocket frames into a byte stream /// for sending on the network. diff --git a/Tests/NIOTests/ByteBufferTest.swift b/Tests/NIOTests/ByteBufferTest.swift index 2ccc3243..66f33cc0 100644 --- a/Tests/NIOTests/ByteBufferTest.swift +++ b/Tests/NIOTests/ByteBufferTest.swift @@ -1073,22 +1073,37 @@ class ByteBufferTest: XCTestCase { } func testAllocationOfReallyBigByteBuffer() throws { - let alloc = ByteBufferAllocator(hookedMalloc: { testAllocationOfReallyBigByteBuffer_mallocHook($0) }, - hookedRealloc: { testAllocationOfReallyBigByteBuffer_reallocHook($0, $1) }, - hookedFree: { testAllocationOfReallyBigByteBuffer_freeHook($0) }, - hookedMemcpy: { testAllocationOfReallyBigByteBuffer_memcpyHook($0, $1, $2) }) + #if arch(arm) // 32-bit, Raspi/AppleWatch/etc + // FIXME: Fails hard on Raspi 32-bit even with Int16.max in `__memcpy_neon`. Figure out how and why. + XCTAssertTrue(false, "testAllocationOfReallyBigByteBuffer fails on 32-bit Raspi") + #else + let alloc = ByteBufferAllocator(hookedMalloc: { testAllocationOfReallyBigByteBuffer_mallocHook($0) }, + hookedRealloc: { testAllocationOfReallyBigByteBuffer_reallocHook($0, $1) }, + hookedFree: { testAllocationOfReallyBigByteBuffer_freeHook($0) }, + hookedMemcpy: { testAllocationOfReallyBigByteBuffer_memcpyHook($0, $1, $2) }) - XCTAssertEqual(AllocationExpectationState.begin, testAllocationOfReallyBigByteBuffer_state) - var buf = alloc.buffer(capacity: Int(Int32.max)) - XCTAssertEqual(AllocationExpectationState.mallocDone, testAllocationOfReallyBigByteBuffer_state) - XCTAssertGreaterThanOrEqual(buf.capacity, Int(Int32.max)) + #if arch(arm) // 32-bit, Raspi/AppleWatch/etc + let reallyBigSize = Int(Int16.max) // well, but Int32 is too big (1GB RAM, no swap) + #else + let reallyBigSize = Int(Int32.max) + #endif + XCTAssertEqual(AllocationExpectationState.begin, testAllocationOfReallyBigByteBuffer_state) + var buf = alloc.buffer(capacity: reallyBigSize) + XCTAssertEqual(AllocationExpectationState.mallocDone, testAllocationOfReallyBigByteBuffer_state) + XCTAssertGreaterThanOrEqual(buf.capacity, reallyBigSize) - buf.set(bytes: [1], at: 0) - /* now make it expand (will trigger realloc) */ - buf.set(bytes: [1], at: buf.capacity) + buf.set(bytes: [1], at: 0) + /* now make it expand (will trigger realloc) */ + buf.set(bytes: [1], at: buf.capacity) - XCTAssertEqual(AllocationExpectationState.reallocDone, testAllocationOfReallyBigByteBuffer_state) - XCTAssertEqual(buf.capacity, Int(UInt32.max)) + XCTAssertEqual(AllocationExpectationState.reallocDone, testAllocationOfReallyBigByteBuffer_state) + #if arch(arm) // 32-bit, Raspi/AppleWatch/etc + // TODO(hh): no idea, but not UInt32.max :-) + XCTAssertGreaterThanOrEqual(buf.capacity, reallyBigSize) + #else + XCTAssertEqual(buf.capacity, Int(UInt32.max)) + #endif + #endif } func testWritableBytesAccountsForSlicing() throws { diff --git a/Tests/NIOTests/ChannelTests.swift b/Tests/NIOTests/ChannelTests.swift index df3b8947..81d505a8 100644 --- a/Tests/NIOTests/ChannelTests.swift +++ b/Tests/NIOTests/ChannelTests.swift @@ -157,9 +157,15 @@ public class ChannelTests: XCTestCase { for _ in 0..] = Array(repeating: Unmanaged.passUnretained(o), count: Socket.writevLimitIOVectors + 1) /* put a canary value at the end */ - iovecs[iovecs.count - 1] = iovec(iov_base: UnsafeMutableRawPointer(bitPattern: 0xdeadbeef)!, iov_len: 0xdeadbeef) + iovecs[iovecs.count - 1] = iovec(iov_base: UnsafeMutableRawPointer(bitPattern: 0x1337beef)!, iov_len: 0x1337beef) try iovecs.withUnsafeMutableBufferPointer { iovecs in try managed.withUnsafeMutableBufferPointer { managed in let pwm = NIO.PendingStreamWritesManager(iovecs: iovecs, storageRefs: managed) @@ -225,8 +231,8 @@ public class ChannelTests: XCTestCase { } /* assert that the canary values are still okay, we should definitely have never written those */ XCTAssertEqual(managed.last!.toOpaque(), Unmanaged.passUnretained(o).toOpaque()) - XCTAssertEqual(0xdeadbeef, Int(bitPattern: iovecs.last!.iov_base)) - XCTAssertEqual(0xdeadbeef, iovecs.last!.iov_len) + XCTAssertEqual(0x1337beef, Int(bitPattern: iovecs.last!.iov_base)) + XCTAssertEqual(0x1337beef, iovecs.last!.iov_len) } } @@ -664,8 +670,8 @@ public class ChannelTests: XCTestCase { /// Test that with a few massive buffers, we don't offer more than we should to `writev` if the individual chunks fit. func testPendingWritesNoMoreThanWritevLimitIsWritten() throws { let el = EmbeddedEventLoop() - let alloc = ByteBufferAllocator(hookedMalloc: { _ in UnsafeMutableRawPointer(bitPattern: 0xdeadbeef)! }, - hookedRealloc: { _, _ in UnsafeMutableRawPointer(bitPattern: 0xdeadbeef)! }, + let alloc = ByteBufferAllocator(hookedMalloc: { _ in UnsafeMutableRawPointer(bitPattern: 0x1337beef)! }, + hookedRealloc: { _, _ in UnsafeMutableRawPointer(bitPattern: 0x1337beef)! }, hookedFree: { _ in }, hookedMemcpy: { _, _, _ in }) /* each buffer is half the writev limit */ @@ -696,8 +702,8 @@ public class ChannelTests: XCTestCase { /// Test that with a massive buffers (bigger than writev size), we don't offer more than we should to `writev`. func testPendingWritesNoMoreThanWritevLimitIsWrittenInOneMassiveChunk() throws { let el = EmbeddedEventLoop() - let alloc = ByteBufferAllocator(hookedMalloc: { _ in UnsafeMutableRawPointer(bitPattern: 0xdeadbeef)! }, - hookedRealloc: { _, _ in UnsafeMutableRawPointer(bitPattern: 0xdeadbeef)! }, + let alloc = ByteBufferAllocator(hookedMalloc: { _ in UnsafeMutableRawPointer(bitPattern: 0x1337beef)! }, + hookedRealloc: { _, _ in UnsafeMutableRawPointer(bitPattern: 0x1337beef)! }, hookedFree: { _ in }, hookedMemcpy: { _, _, _ in }) /* each buffer is half the writev limit */ diff --git a/Tests/NIOTests/DatagramChannelTests.swift b/Tests/NIOTests/DatagramChannelTests.swift index 8726b92a..0c69a712 100644 --- a/Tests/NIOTests/DatagramChannelTests.swift +++ b/Tests/NIOTests/DatagramChannelTests.swift @@ -244,8 +244,13 @@ final class DatagramChannelTests: XCTestCase { } let envelope = AddressedEnvelope(remoteAddress: self.secondChannel.localAddress!, data: buffer) + #if arch(arm) // 32-bit, Raspi/AppleWatch/etc + let lotsOfData = Int(Int32.max / 8) + #else + let lotsOfData = Int(Int32.max) + #endif var written = 0 - while written <= Int(INT32_MAX) { + while written <= lotsOfData { self.firstChannel.write(NIOAny(envelope), promise: myPromise) overall = EventLoopFuture.andAll([overall, myPromise.futureResult], eventLoop: self.firstChannel.eventLoop) written += bufferSize diff --git a/Tests/NIOTests/EmbeddedEventLoopTest.swift b/Tests/NIOTests/EmbeddedEventLoopTest.swift index 450eb385..4602a1e7 100644 --- a/Tests/NIOTests/EmbeddedEventLoopTest.swift +++ b/Tests/NIOTests/EmbeddedEventLoopTest.swift @@ -110,7 +110,7 @@ public class EmbeddedEventLoopTest: XCTestCase { var sentinel = 0 let loop = EmbeddedEventLoop() for index in 1...10 { - _ = loop.scheduleTask(in: .nanoseconds(index)) { + _ = loop.scheduleTask(in: .nanoseconds(TimeAmount.Value(index))) { sentinel = index } } diff --git a/Tests/NIOTests/PendingDatagramWritesManagerTests.swift b/Tests/NIOTests/PendingDatagramWritesManagerTests.swift index 9fe1c753..dd1c6002 100644 --- a/Tests/NIOTests/PendingDatagramWritesManagerTests.swift +++ b/Tests/NIOTests/PendingDatagramWritesManagerTests.swift @@ -62,7 +62,7 @@ class PendingDatagramWritesManagerTests: XCTestCase { var msgs: [MMsgHdr] = Array(repeating: MMsgHdr(), count: Socket.writevLimitIOVectors + 1) var addresses: [sockaddr_storage] = Array(repeating: sockaddr_storage(), count: Socket.writevLimitIOVectors + 1) /* put a canary value at the end */ - iovecs[iovecs.count - 1] = iovec(iov_base: UnsafeMutableRawPointer(bitPattern: 0xdeadbeef)!, iov_len: 0xdeadbeef) + iovecs[iovecs.count - 1] = iovec(iov_base: UnsafeMutableRawPointer(bitPattern: 0x1337beef)!, iov_len: 0x1337beef) try iovecs.withUnsafeMutableBufferPointer { iovecs in try managed.withUnsafeMutableBufferPointer { managed in try msgs.withUnsafeMutableBufferPointer { msgs in @@ -83,8 +83,8 @@ class PendingDatagramWritesManagerTests: XCTestCase { } /* assert that the canary values are still okay, we should definitely have never written those */ XCTAssertEqual(managed.last!.toOpaque(), Unmanaged.passUnretained(o).toOpaque()) - XCTAssertEqual(0xdeadbeef, Int(bitPattern: iovecs.last!.iov_base)) - XCTAssertEqual(0xdeadbeef, iovecs.last!.iov_len) + XCTAssertEqual(0x1337beef, Int(bitPattern: iovecs.last!.iov_base)) + XCTAssertEqual(0x1337beef, iovecs.last!.iov_len) } } @@ -433,8 +433,8 @@ class PendingDatagramWritesManagerTests: XCTestCase { /// Test that with a few massive buffers, we don't offer more than we should to `writev` if the individual chunks fit. func testPendingWritesNoMoreThanWritevLimitIsWritten() throws { let el = EmbeddedEventLoop() - let alloc = ByteBufferAllocator(hookedMalloc: { _ in return UnsafeMutableRawPointer(bitPattern: 0xdeadbeef)! }, - hookedRealloc: { _, _ in return UnsafeMutableRawPointer(bitPattern: 0xdeadbeef)! }, + let alloc = ByteBufferAllocator(hookedMalloc: { _ in return UnsafeMutableRawPointer(bitPattern: 0xdeadbeef as UInt)! }, + hookedRealloc: { _, _ in return UnsafeMutableRawPointer(bitPattern: 0xdeadbeef as UInt)! }, hookedFree: { _ in }, hookedMemcpy: { _, _, _ in }) /* each buffer is half the writev limit */ @@ -466,8 +466,8 @@ class PendingDatagramWritesManagerTests: XCTestCase { func testPendingWritesNoMoreThanWritevLimitIsWrittenInOneMassiveChunk() throws { let el = EmbeddedEventLoop() let address = try SocketAddress(ipAddress: "127.0.0.1", port: 65535) - let alloc = ByteBufferAllocator(hookedMalloc: { _ in return UnsafeMutableRawPointer(bitPattern: 0xdeadbeef)! }, - hookedRealloc: { _, _ in return UnsafeMutableRawPointer(bitPattern: 0xdeadbeef)! }, + let alloc = ByteBufferAllocator(hookedMalloc: { _ in return UnsafeMutableRawPointer(bitPattern: 0xdeadbeef as UInt)! }, + hookedRealloc: { _, _ in return UnsafeMutableRawPointer(bitPattern: 0xdeadbeef as UInt)! }, hookedFree: { _ in }, hookedMemcpy: { _, _, _ in }) /* each buffer is half the writev limit */ diff --git a/Tests/NIOTests/SystemCallWrapperHelpers.swift b/Tests/NIOTests/SystemCallWrapperHelpers.swift index 7a9bb5fe..d144432d 100644 --- a/Tests/NIOTests/SystemCallWrapperHelpers.swift +++ b/Tests/NIOTests/SystemCallWrapperHelpers.swift @@ -72,7 +72,7 @@ func runSystemCallWrapperPerformanceTest(testAssertFunction: (@autoclosure () -> } let iterations = isDebugMode ? 100_000 : 1_000_000 - let pointer = UnsafePointer(bitPattern: 0xdeadbeef)! + let pointer = UnsafePointer(bitPattern: 0x1337beef)! let directCallTime = try measureRunTime { () -> Int in /* imitate what the system call wrappers do to have a fair comparison */