920b36cdd6
Four perf wins from the round-4 audit, all in the steady-state hot path (audio rooms run at ~50 datagrams/sec/participant). Perf #1 — ACK frame gating (saves a frame + bandwidth on every drain): AckTracker.buildAckFrame now returns null when no new ack-eliciting packet has arrived since the last build. Pre-fix the writer emitted a redundant ACK frame on every outbound packet (~50/sec each direction) even when the only inbound traffic since last drain was ACK-only. RFC 9000 §13.2 only requires ACKs in response to ack-eliciting packets within max_ack_delay; gating on ackElicitingPending satisfies that without delay-timer machinery. Perf #11 — AckTracker.purgeBelow short-circuit: Common case: peer ACKs a high PN, we already pruned below it. Pre-fix triggered a full ListIterator walk anyway. Now bails out when the tail's start is already above the threshold. Perf #9 — Flow-control dirty-set: QuicStream gains a receiveDirtyForFlowControl flag set by the parser when readContiguous advances the frontier. The writer's appendFlowControlUpdates now skips per-direction-window lookup + threshold comparison for streams whose flag is unset. Big win for multi-stream sessions (audio rooms with N×M streams per participant). Perf #10 — Streams list view: QuicConnection maintains a parallel insertion-ordered List<QuicStream> alongside the streams Map. Writer's round-robin scan reads the list directly instead of allocating `entries.toList()` per drain. No removal path exists today; the insert points (openBidi/UniStream, getOrCreatePeerStreamLocked) update both. AckTrackerGatingTest pins the new gating contract: first build returns a frame; second build without new reception returns null; subsequent ack-eliciting reception re-arms; non-ack-eliciting receptions alone don't. https://claude.ai/code/session_01EC1tfXfap8k8GyKvrxkxZx