buildAckFromSet function

QuicAckFrame buildAckFromSet(
  1. Set<int> received, {
  2. int ackDelayMicros = 0,
  3. int ackDelayExponent = 3,
  4. int? ect0,
  5. int? ect1,
  6. int? ce,
})

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,
  );
}