Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 17 additions & 10 deletions Sources/SwiftNetwork/Protocols/Frame.swift
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ public struct Frame: ~Copyable {
if adjustSingleIPAggregate && isSingleIPAggregate {
guard fromEnd == 0 else {
#if !DisableErrorLogging
Logger.proto.error("Trying to claim at the end \(fromEnd) bytes from a single-IP aggregate")
outlinedProtoLogError("Trying to claim bytes at the end of a single-IP aggregate", fromEnd)
#endif
return false
}
Expand All @@ -301,8 +301,11 @@ public struct Frame: ~Copyable {
guard newStart <= effectiveBufferLength - newEnd else {
let effectiveLength = effectiveBufferLength
#if !DisableErrorLogging
Logger.proto.error(
"Claiming bytes failed because start (\(newStart)) is beyond end (\(effectiveLength) - \(newEnd))"
outlinedProtoLogError(
"Claiming bytes failed, start is beyond end; start, effective length, end",
newStart,
effectiveLength,
newEnd
)
#endif
return false
Expand All @@ -322,7 +325,7 @@ public struct Frame: ~Copyable {
if adjustSingleIPAggregate && isSingleIPAggregate {
guard fromEnd == 0 else {
#if !DisableErrorLogging
Logger.proto.error("Trying to unclaim at the end \(fromEnd) bytes from a single-IP aggregate")
outlinedProtoLogError("Trying to unclaim bytes at the end of a single-IP aggregate", fromEnd)
#endif
return false
}
Expand All @@ -332,15 +335,15 @@ public struct Frame: ~Copyable {
guard fromStart <= startOffset else {
let startOffset = startOffset
#if !DisableErrorLogging
Logger.proto.error("Frame cannot unclaim \(fromStart) start bytes (has \(startOffset) left)")
outlinedProtoLogError("Frame cannot unclaim start bytes; requested, remaining", fromStart, startOffset)
#endif
return false
}

guard fromEnd <= endOffset else {
let endOffset = endOffset
#if !DisableErrorLogging
Logger.proto.error("Frame cannot unclaim \(fromEnd) end bytes (has \(endOffset) left)")
outlinedProtoLogError("Frame cannot unclaim end bytes; requested, remaining", fromEnd, endOffset)
#endif
return false
}
Expand Down Expand Up @@ -642,14 +645,14 @@ public struct Frame: ~Copyable {
var packetChainTotalLength: Int {
get {
guard isSingleIPAggregate else {
Logger.proto.fault("Attempt to get aggregate buffer length on a non-single IP aggregate")
outlinedProtoLogFault("Attempt to get aggregate buffer length on a non-single IP aggregate")
return 0
}
return aggregateBufferLength
}
set {
guard isSingleIPAggregate else {
Logger.proto.fault("Attempt to set aggregate buffer length on a non-single IP aggregate")
outlinedProtoLogFault("Attempt to set aggregate buffer length on a non-single IP aggregate")
return
}
aggregateBufferLength = newValue
Expand All @@ -667,7 +670,7 @@ public struct Frame: ~Copyable {
}
guard newValue < 64 else {
#if !DisableErrorLogging
Logger.proto.error("Cannot set DSCP value of \(newValue)")
outlinedProtoLogError("Cannot set DSCP value", newValue)
#endif
return
}
Expand Down Expand Up @@ -856,7 +859,11 @@ public struct Frame: ~Copyable {
guard length >= 0 else { return }
guard length <= aggregateBufferLength else {
let existingLength = aggregateBufferLength
Logger.proto.fault("Aggregate buffer length \(existingLength) cannot remove \(length)")
outlinedProtoLogFault(
"Aggregate buffer length cannot remove requested bytes; existing, requested",
existingLength,
length
)
aggregateBufferLength = 0
return
}
Expand Down
32 changes: 22 additions & 10 deletions Sources/SwiftNetwork/QUIC/Ack.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1027,14 +1027,20 @@ struct AckBitstring: ~Copyable {
// The connection will stall if these two conditions occur.
let initialWord = initialWord
if _slowPath(startWord < initialWord) {
Logger.proto.fault(
"Initial word \(initialWord) is lower than start \(startWord) (pn \(start))"
outlinedProtoLogFault(
"Initial word is lower than start; initial, start, packet number",
initialWord,
startWord,
start.value
)
return false
}
if _slowPath(stopWord < initialWord) {
Logger.proto.fault(
"Initial word \(initialWord) is lower than stop \(stopWord) (pn \(stop))"
outlinedProtoLogFault(
"Initial word is lower than stop; initial, stop, packet number",
initialWord,
stopWord,
stop.value
)
return false
}
Expand All @@ -1053,14 +1059,20 @@ struct AckBitstring: ~Copyable {
let bitstringCount = UInt64(bitstring.count)
let initialWord = initialWord
if _slowPath(startWord > initialWord + bitstringCount) {
Logger.proto.fault(
"Size \(bitstringCount + initialWord) is lower than start \(startWord) (pn \(start))"
outlinedProtoLogFault(
"Bitstring size is lower than start; size, start, packet number",
bitstringCount + initialWord,
startWord,
start.value
)
return false
}
if _slowPath(stopWord > initialWord + bitstringCount) {
Logger.proto.fault(
"Size \(bitstringCount + initialWord) is lower than start \(stopWord) (pn \(stop))"
outlinedProtoLogFault(
"Bitstring size is lower than stop; size, stop, packet number",
bitstringCount + initialWord,
stopWord,
stop.value
)
return false
}
Expand All @@ -1078,7 +1090,7 @@ struct AckBitstring: ~Copyable {

if stopWord >= size {
guard _slowPath(stopWord < UInt32.max / 2) else {
Logger.proto.info("Refusing to grow bitstring further")
outlinedProtoLogInfo("Refusing to grow bitstring further")
return
}
let targetSize = Int(stopWord) + 1
Expand Down Expand Up @@ -1124,7 +1136,7 @@ struct AckBitstring: ~Copyable {
guard initialWord == other.initialWord else {
let initialWord = self.initialWord
let otherInitialWord = other.initialWord
Logger.proto.fault("Bitstring initial mismatch \(initialWord) != \(otherInitialWord)")
outlinedProtoLogFault("Bitstring initial mismatch; self, other", initialWord, otherInitialWord)
return AckBitstringSequence.empty
}
if size > other.size {
Expand Down
8 changes: 2 additions & 6 deletions Sources/SwiftNetwork/QUIC/Packet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -307,9 +307,7 @@ struct Packet: ~Copyable {
get {
if let _overrideSentNumberSize {
#if !DisableErrorLogging
Logger.proto.error(
"WARNING: Reading overrideSentNumberSize only be used for unit testing!"
)
outlinedProtoLogError("WARNING: Reading overrideSentNumberSize only be used for unit testing!")
#endif
return _overrideSentNumberSize
}
Expand All @@ -318,9 +316,7 @@ struct Packet: ~Copyable {
set(newValue) {
if let newValue {
#if !DisableErrorLogging
Logger.proto.error(
"WARNING: Setting overrideSentNumberSize only be used for unit testing!"
)
outlinedProtoLogError("WARNING: Setting overrideSentNumberSize only be used for unit testing!")
#endif
_overrideSentNumberSize = newValue
} else {
Expand Down
5 changes: 3 additions & 2 deletions Sources/SwiftNetwork/QUIC/PacketNumberSpace.swift
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ struct PacketNumber: Comparable, ExpressibleByIntegerLiteral, Hashable, CustomSt
return (bits + 7) / 8
}

@inline(always)
func encode(
lastAcked: PacketNumber,
fixedSize: EncodedPacketNumber.Size? = nil
Expand All @@ -207,7 +208,7 @@ struct PacketNumber: Comparable, ExpressibleByIntegerLiteral, Hashable, CustomSt
// check for packet number not greater than lastAcked.
// If lastAcked == .none the peer has not yet acknowledged anything in this packet number space
if lastAcked != .none, self <= lastAcked {
Logger.proto.error("Ack number underflow: \(self) <= \(lastAcked)")
outlinedProtoLogError("Ack number underflow; number, lastAcked", self.value, lastAcked.value)
throw QUICError.packet(QUICPacketError.ackNumberUnderflow)
}

Expand All @@ -217,7 +218,7 @@ struct PacketNumber: Comparable, ExpressibleByIntegerLiteral, Hashable, CustomSt
var truncatedPacketNumber = self.value
var size: EncodedPacketNumber.Size
if let fixedSize {
Logger.proto.error("WARNING: Use overrideSentNumberSize only for unit testing!")
outlinedProtoLogError("WARNING: Use overrideSentNumberSize only for unit testing!")
size = fixedSize
} else {
if difference <= 0xff {
Expand Down
11 changes: 3 additions & 8 deletions Sources/SwiftNetwork/QUIC/Protector.swift
Original file line number Diff line number Diff line change
Expand Up @@ -845,14 +845,9 @@ struct Protector: ~Copyable, PrefixedLoggable {
// Packet number offset must always be set, otherwise sampleRange doesn't work before we get here.
packetNumberOffset = packet.packetNumberOffset!
} else {
// When sealing/encrypting, the packet number is known but may be overridden
if let length = packet.overrideSentNumberSize?.rawValue {
packetNumberLength = length
} else {
// The packet number would not have been written if it doesn't encode
packetNumberLength = try! packet.number.encode(lastAcked: packet.lastAcked).size

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

.rawValue
}
// When sealing/encrypting, writing the header recorded the encoded length, including
// any override of the number of bytes it occupies.
packetNumberLength = Int(packet.packetNumberLength)
packetNumberOffset = packet.packetNumberOffset!
}

Expand Down
133 changes: 74 additions & 59 deletions Sources/SwiftNetwork/Utilities/Deserializer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ public struct Deserializer<Factory: DeserializerSpanFactory & ~Copyable & ~Escap
///
/// Call this method when `hasRoom` fails but `internalResult` is still valid.
@inlinable
@inline(always)
@inline(never)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why never here?

mutating func readFragmented<T: BitwiseCopyable>(_ value: inout T) throws(DeserializationError) {
let length = MemoryLayout<T>.size
precondition(length <= 16)
Expand Down Expand Up @@ -715,6 +715,45 @@ public struct Deserializer<Factory: DeserializerSpanFactory & ~Copyable & ~Escap
moveCursorUnchecked(length)
}

/// Compares across span boundaries, refilling from the factory as each span is exhausted.
@usableFromInline
@inline(never)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why inlined never here if the calling function is inlined?

mutating func spanExpectFragmented(expect value: RawSpan, length: Int) throws(DeserializationError) {
// Fast fail if total available bytes across all spans is insufficient
guard availableByteCount - totalBytesParsed >= length else {
try invalidate(.bufferTooShort)
}

var matched = 0
while matched < length {
let available = min(remaining, length - matched)
if available > 0 {
let matches = value.withUnsafeBytes { expectedBytes in
let slice = UnsafeRawBufferPointer(
start: expectedBytes.baseAddress! + matched,
count: available
)
return Deserializer.valueMatches(
lhs: slice,
rhs: currentSpan,
rhsOffset: cursor,
count: available
)
}
guard matches else {
try invalidate(.validationFailed)
}
try moveCursor(available)
matched += available
}
if matched < length {
guard refill() else {
try invalidate(.bufferTooShort)
}
}
}
}

@inlinable
@inline(always)
public mutating func span(expect value: RawSpan) throws(DeserializationError) {
Expand All @@ -724,40 +763,7 @@ public struct Deserializer<Factory: DeserializerSpanFactory & ~Copyable & ~Escap
}

guard hasRoom(length) else {
// Fast fail if total available bytes across all spans is insufficient
guard availableByteCount - totalBytesParsed >= length else {
try invalidate(.bufferTooShort)
}

// Compare across span boundaries
var matched = 0
while matched < length {
let available = min(remaining, length - matched)
if available > 0 {
let matches = value.withUnsafeBytes { expectedBytes in
let slice = UnsafeRawBufferPointer(
start: expectedBytes.baseAddress! + matched,
count: available
)
return Deserializer.valueMatches(
lhs: slice,
rhs: currentSpan,
rhsOffset: cursor,
count: available
)
}
guard matches else {
try invalidate(.validationFailed)
}
try moveCursor(available)
matched += available
}
if matched < length {
guard refill() else {
try invalidate(.bufferTooShort)
}
}
}
try spanExpectFragmented(expect: value, length: length)
return
}

Expand All @@ -771,6 +777,38 @@ public struct Deserializer<Factory: DeserializerSpanFactory & ~Copyable & ~Escap
moveCursorUnchecked(length)
}

/// Reads across span boundaries directly into the destination.
@usableFromInline
@inline(never)
mutating func spanFragmented(
_ value: inout MutableSpan<UInt8>,
lengthToCopy: Int
) throws(DeserializationError) {
var filled = 0
while filled < lengthToCopy {
let available = min(remaining, lengthToCopy - filled)
if available > 0 {
let source = currentSpan.extracting(unchecked: cursor..<(cursor &+ available))
source.withUnsafeBytes { fromBuffer in
value.withUnsafeMutableBytes { toBuffer in
let dest = UnsafeMutableRawBufferPointer(
start: toBuffer.baseAddress! + filled,
count: available
)
dest.copyMemory(from: fromBuffer)
}
}
try moveCursor(available)
filled += available
}
if filled < lengthToCopy {
guard refill() else {
try invalidate(.bufferTooShort)
}
}
}
}

@_optimize(speed)
@inlinable
@inline(always)
Expand All @@ -790,30 +828,7 @@ public struct Deserializer<Factory: DeserializerSpanFactory & ~Copyable & ~Escap
}

guard hasRoom(lengthToCopy) else {
// Read across spans directly into the destination
var filled = 0
while filled < lengthToCopy {
let available = min(remaining, lengthToCopy - filled)
if available > 0 {
let source = currentSpan.extracting(unchecked: cursor..<(cursor &+ available))
source.withUnsafeBytes { fromBuffer in
value.withUnsafeMutableBytes { toBuffer in
let dest = UnsafeMutableRawBufferPointer(
start: toBuffer.baseAddress! + filled,
count: available
)
dest.copyMemory(from: fromBuffer)
}
}
try moveCursor(available)
filled += available
}
if filled < lengthToCopy {
guard refill() else {
try invalidate(.bufferTooShort)
}
}
}
try spanFragmented(&value, lengthToCopy: lengthToCopy)
return
}

Expand Down
Loading
Loading