diff --git a/Sources/SwiftNetwork/QUIC/SendItems.swift b/Sources/SwiftNetwork/QUIC/SendItems.swift index 3f2017b..54da19f 100644 --- a/Sources/SwiftNetwork/QUIC/SendItems.swift +++ b/Sources/SwiftNetwork/QUIC/SendItems.swift @@ -3110,19 +3110,78 @@ struct TransmittedItems: ~Copyable { var sentCrypto = NetworkUniqueArray() // Minimal information about a send on a stream - struct SentStream: ~Copyable { + struct SentStream { let flowID: MultiplexedFlowIdentifier let streamID: QUICStreamID let offset: UInt64 let length: UInt64 let isFinal: Bool - func matches(_ other: borrowing SentStream) -> Bool { + func matches(_ other: SentStream) -> Bool { flowID == other.flowID && streamID == other.streamID && offset == other.offset && length == other.length && isFinal == other.isFinal } } - var sentStreams = NetworkUniqueArray() + + /// The stream sends a packet carried. + /// + /// A packet almost always carries a single stream, and every sent packet keeps its record until it is + /// acknowledged or lost, so one send is held inline and only a second one allocates storage. + struct SentStreams: ~Copyable { + private enum Storage: ~Copyable { + case empty + case one(SentStream) + case many(NetworkUniqueArray) + } + + private var storage = Storage.empty + + var isEmpty: Bool { + switch storage { + case .empty: return true + case .one: return false + case .many(let sends): return sends.isEmpty + } + } + + var count: Int { + switch storage { + case .empty: return 0 + case .one: return 1 + case .many(let sends): return sends.count + } + } + + subscript(index: Int) -> SentStream { + switch storage { + case .empty: + preconditionFailure("Index out of range") + case .one(let send): + precondition(index == 0, "Index out of range") + return send + case .many(let sends): + return sends[index] + } + } + + mutating func append(_ sentStream: SentStream) { + var taken = Storage.empty + swap(&taken, &storage) + switch consume taken { + case .empty: + storage = .one(sentStream) + case .one(let first): + var sends = NetworkUniqueArray(minimumCapacity: 2) + sends.append(first) + sends.append(sentStream) + storage = .many(sends) + case .many(var sends): + sends.append(sentStream) + storage = .many(sends) + } + } + } + var sentStreams = SentStreams() var maxStreamDataFlows = Deque() var streamDataBlockedFlows = Deque() diff --git a/Tests/QUICTests/QUICLayoutTests.swift b/Tests/QUICTests/QUICLayoutTests.swift index a33a314..a6741a3 100644 --- a/Tests/QUICTests/QUICLayoutTests.swift +++ b/Tests/QUICTests/QUICLayoutTests.swift @@ -31,7 +31,7 @@ final class QUICLayoutTests: XCTestCase { func testLayoutPacket() { let packetSize = 156 - let packetRecordSize = 193 + let packetRecordSize = 209 XCTAssertEqual(packetSize, MemoryLayout.size) XCTAssertEqual(packetRecordSize, MemoryLayout.size) }