buildAckFromSet function
Implementation
QuicAckFrame buildAckFromSet(
Set<int> received, {
int ackDelayMicros = 0,
int ackDelayExponent = 3, // default QUIC exponent if peer didn't override
int? ect0,
int? ect1,
int? ce,
}) {
if (received.isEmpty) {
return QuicAckFrame(
largest: 0,
ackDelay: 0,
firstRange: 0,
additionalRanges: [],
ect0: ect0,
ect1: ect1,
ce: ce,
);
}
final sorted = received.toList()..sort();
final largest = sorted.last;
int idx = sorted.length - 1;
// ----------------------------------------------------------
// First ACK range:
// largest contiguous run downward from the largest packet
// firstRange is encoded as range_size - 1
// ----------------------------------------------------------
int firstRangeLen = 0;
while (idx > 0 && sorted[idx - 1] == sorted[idx] - 1) {
firstRangeLen++;
idx--;
}
// ----------------------------------------------------------
// Additional ACK ranges
// ----------------------------------------------------------
final ranges = <QuicAckRange>[];
int cursor = idx - 1;
// previousRangeSmallest tracks the smallest packet number
// in the previously emitted ACK range.
int previousRangeSmallest = largest - firstRangeLen;
while (cursor >= 0) {
final rangeEnd = sorted[cursor];
// Walk backward to find the contiguous start of this range
int rangeStartIndex = cursor;
while (rangeStartIndex > 0 &&
sorted[rangeStartIndex - 1] == sorted[rangeStartIndex] - 1) {
rangeStartIndex--;
}
final rangeStartPn = sorted[rangeStartIndex];
// Number of packets in this contiguous range
final rangeSize = rangeEnd - rangeStartPn + 1;
// QUIC ACK rangeLength = range_size - 1
final rangeLength = rangeSize - 1;
// Missing packets between ranges:
// previousRangeSmallest ... next rangeEnd
//
// Example:
// previous smallest = 98
// next range end = 95
// missing packets = 97,96 => count = 2
final missingCount = previousRangeSmallest - rangeEnd - 1;
// QUIC gap field = missingCount - 1
final gap = missingCount - 1;
if (missingCount <= 0 || gap < 0) {
throw StateError(
'Invalid ACK range construction: '
'previousSmallest=$previousRangeSmallest '
'rangeEnd=$rangeEnd missingCount=$missingCount gap=$gap',
);
}
ranges.add(QuicAckRange(gap: gap, rangeLength: rangeLength));
previousRangeSmallest = rangeStartPn;
cursor = rangeStartIndex - 1;
}
// QUIC ACK delay is encoded in units of 2^ackDelayExponent microseconds
final encodedAckDelay = ackDelayMicros >> ackDelayExponent;
return QuicAckFrame(
largest: largest,
ackDelay: encodedAckDelay,
firstRange: firstRangeLen,
additionalRanges: ranges,
ect0: ect0,
ect1: ect1,
ce: ce,
);
}