docs: async def _receive_sack_chunk(self, chunk):
          """
          Handle a SACK chunk.
          """
          if uint32_gt(self._last_sacked_tsn, chunk.cumulative_tsn):
              return
  
          received_time = time.time()
          self._last_sacked_tsn = chunk.cumulative_tsn
          cwnd_fully_utilized = (self._flight_size >= self._cwnd)
          done = 0
          done_bytes = 0
  
          # handle acknowledged data
          while self._sent_queue and uint32_gte(self._last_sacked_tsn, self._sent_queue[0].tsn):
              schunk = self._sent_queue.popleft()
              done += 1
              if not schunk._acked:
                  done_bytes += schunk._book_size
                  self._flight_size_decrease(schunk)
  
              # update RTO estimate
              if done == 1 and schunk._sent_count == 1:
                  self._update_rto(received_time - schunk._sent_time)
  
          # handle gap blocks
          loss = False
          if chunk.gaps:
              seen = set()
              for gap in chunk.gaps:
                  for pos in range(gap[0], gap[1] + 1):
                      highest_seen_tsn = (chunk.cumulative_tsn + pos) % SCTP_TSN_MODULO
                      seen.add(highest_seen_tsn)
  
              # determined Highest TSN Newly Acked (HTNA)
              highest_newly_acked = chunk.cumulative_tsn
              for schunk in self._sent_queue:
                  if uint32_gt(schunk.tsn, highest_seen_tsn):
                      break
                  if schunk.tsn in seen and not schunk._acked:
                      done_bytes += schunk._book_size
                      schunk._acked = True
                      self._flight_size_decrease(schunk)
                      highest_newly_acked = schunk.tsn
  
              # strike missing chunks prior to HTNA
              for schunk in self._sent_queue:
                  if uint32_gt(schunk.tsn, highest_newly_acked):
                      break
                  if schunk.tsn not in seen:
                      schunk._misses += 1
                      if schunk._misses == 3:
                          schunk._misses = 0
                          if not self._maybe_abandon(schunk):
                              schunk._retransmit = True
  
                          schunk._acked = False
                          self._flight_size_decrease(schunk)
  
                          loss = True
  
          # adjust congestion window
          if self._fast_recovery_exit is None:
              if done and cwnd_fully_utilized:
                  if self._cwnd <= self._ssthresh:
                      # slow start
                      self._cwnd += min(done_bytes, USERDATA_MAX_LENGTH)
                  else:
                      # congestion avoidance
                      self._partial_bytes_acked += done_bytes
                      if self._partial_bytes_acked >= self._cwnd:
                          self._partial_bytes_acked -= self._cwnd
                          self._cwnd += USERDATA_MAX_LENGTH
              if loss:
                  self._ssthresh = max(self._cwnd // 2, 4 * USERDATA_MAX_LENGTH)
                  self._cwnd = self._ssthresh
                  self._partial_bytes_acked = 0
                  self._fast_recovery_exit = self._sent_queue[-1].tsn
                  self._fast_recovery_transmit = True
          elif uint32_gte(chunk.cumulative_tsn, self._fast_recovery_exit):
              self._fast_recovery_exit = None
  
          if not self._sent_queue:
              # there is no outstanding data, stop T3
              self._t3_cancel()
          elif done:
              # the earliest outstanding chunk was acknowledged, restart T3
              self._t3_restart()
  
          self._update_advanced_peer_ack_point()
          await self._data_channel_flush()
          await self._transmit()