nquic_protocol_ack (nquic v1.2.0)
View SourceACK generation and received-packet-number bookkeeping (RFC 9000 §13.2, §19.3).
Pure functions over #conn_state{} covering received-PN range algebra
(insert / merge / prune), per-space received_ranges and
largest_received tracking, ECN counter accounting, ACK-frame
construction, and the ack-eliciting decision that governs whether an
ACK is queued. Queued ACKs drain into the per-encryption-level frame
queues owned by nquic_protocol_send, which this module calls
module-qualified.
Summary
Functions
Apply RFC 9000 §13.2.4 pruning to received_ranges for Space.
Whether the pending application ACK must leave on a flush that carries
no other frame. One ack-eliciting packet can wait for up to
max_ack_delay (RFC 9000 §13.2.1), so that the response of the
application carries the ACK. The ACK leaves at once after two
ack-eliciting packets (§13.2.2), after a packet out of order or after a
gap, after an ECN-CE mark (§13.2.1), and while this endpoint has
ack-eliciting packets in flight. In the last case the peer is in a
transfer and a held ACK stalls its ACK clock.
Drop ranges from received_ranges (descending list of {High, Low}
pairs) whose entire span is at or below Threshold. The range that
straddles Threshold is truncated; ranges entirely above Threshold
are unchanged. Used by apply_received_ranges_prune/3 to apply RFC
9000 §13.2.4.
Queue the pending application ACK as a frame and clear the pending state.
Increment the per-space ECN counter for the given mark.
Fused PN tracking + ECN counter update in a single pn_spaces rewrite.
Replaces track_received_pn/3 followed by track_ecn_mark/3 on the
recv hot path, halving the #conn_state{} record copies and the
outer pn_spaces map updates. Behaviourally equivalent to calling
both helpers in sequence.
Functions
-spec apply_received_ranges_prune(nquic_packet:space(), [nquic_frame:t()], nquic_protocol:state()) -> nquic_protocol:state().
Apply RFC 9000 §13.2.4 pruning to received_ranges for Space.
AckedFrames is the flattened frame list returned by
nquic_loss:on_ack_received/6 for an incoming ACK in Space. Any
#ack{} frames in that list are ACKs we previously sent that the peer
has now acknowledged; their largest_acknowledged value is therefore
known-known on both sides, so the corresponding tracked PNs can be
dropped from our received_ranges. Without this prune received_ranges
grows monotonically across a connection's lifetime and inflates both
ACK build cost and the work the peer does to process those ACKs.
-spec build_ack_for_space(nquic_packet:space(), nquic_protocol:state()) -> {ok, #ack{largest_acknowledged :: nquic_packet_number:t(), delay :: non_neg_integer(), first_ack_range :: non_neg_integer(), ack_ranges :: [#ack_range{gap :: non_neg_integer(), length :: non_neg_integer()}], ecn_counts :: {non_neg_integer(), non_neg_integer(), non_neg_integer()} | undefined}} | none.
-spec insert_pn_range(nquic_packet_number:t(), [{nquic_packet_number:t(), nquic_packet_number:t()}]) -> [{nquic_packet_number:t(), nquic_packet_number:t()}].
-spec is_ack_due(nquic_protocol:state()) -> boolean().
Whether the pending application ACK must leave on a flush that carries
no other frame. One ack-eliciting packet can wait for up to
max_ack_delay (RFC 9000 §13.2.1), so that the response of the
application carries the ACK. The ACK leaves at once after two
ack-eliciting packets (§13.2.2), after a packet out of order or after a
gap, after an ECN-CE mark (§13.2.1), and while this endpoint has
ack-eliciting packets in flight. In the last case the peer is in a
transfer and a held ACK stalls its ACK clock.
-spec maybe_queue_ack(nquic_packet:header(), [nquic_frame:t()], nquic_protocol:state()) -> nquic_protocol:state().
-spec merge_ranges([{nquic_packet_number:t(), nquic_packet_number:t()}]) -> [{nquic_packet_number:t(), nquic_packet_number:t()}].
-spec prune_received_ranges([{nquic_packet_number:t(), nquic_packet_number:t()}], nquic_packet_number:t()) -> [{nquic_packet_number:t(), nquic_packet_number:t()}].
Drop ranges from received_ranges (descending list of {High, Low}
pairs) whose entire span is at or below Threshold. The range that
straddles Threshold is truncated; ranges entirely above Threshold
are unchanged. Used by apply_received_ranges_prune/3 to apply RFC
9000 §13.2.4.
-spec queue_pending_ack(nquic_protocol:state()) -> nquic_protocol:state().
Queue the pending application ACK as a frame and clear the pending state.
-spec ranges_to_ack_ranges(nquic_packet_number:t(), [{nquic_packet_number:t(), nquic_packet_number:t()}]) -> [#ack_range{gap :: non_neg_integer(), length :: non_neg_integer()}].
-spec track_ecn_mark(nquic_packet:space(), nquic_socket:ecn_mark(), nquic_protocol:state()) -> nquic_protocol:state().
Increment the per-space ECN counter for the given mark.
-spec track_received_pn(nquic_packet:space(), nquic_packet_number:t(), nquic_protocol:state()) -> nquic_protocol:state().
-spec track_received_pn_and_ecn(nquic_packet:space(), nquic_packet_number:t(), nquic_socket:ecn_mark(), nquic_protocol:state()) -> nquic_protocol:state().
Fused PN tracking + ECN counter update in a single pn_spaces rewrite.
Replaces track_received_pn/3 followed by track_ecn_mark/3 on the
recv hot path, halving the #conn_state{} record copies and the
outer pn_spaces map updates. Behaviourally equivalent to calling
both helpers in sequence.