cwist
Modern C web framework with libttak runtime
Loading...
Searching...
No Matches
quic_flow_control.c
Go to the documentation of this file.
2
3#define QUIC_MAX_VARINT ((1ULL << 62) - 1)
4#define QUIC_FRAME_MAX_DATA 0x10
5#define QUIC_FRAME_MAX_STREAM_DATA 0x11
6
14static uint64_t clamp_window(uint64_t window) {
16 : window;
17}
18
26static uint64_t saturating_add(uint64_t value, uint64_t increment) {
27 return value > QUIC_MAX_VARINT - increment ? QUIC_MAX_VARINT : value + increment;
28}
29
36static size_t varint_size(uint64_t value) {
37 if (value < (1ULL << 6)) return 1;
38 if (value < (1ULL << 14)) return 2;
39 if (value < (1ULL << 30)) return 4;
40 return 8;
41}
42
54static void write_varint(uint8_t *buf, uint64_t value, size_t size) {
55 size_t i;
56
57 for (i = size; i-- > 0; value >>= 8) buf[i] = (uint8_t)value;
58
59 buf[0] |= (uint8_t)((size == 1 ? 0 : size == 2 ? 1 : size == 4 ? 2 : 3) << 6);
60}
61
73static int should_update(uint64_t consumed, uint64_t maximum, uint64_t window) {
74 return consumed >= maximum || consumed + window / 2 >= maximum;
75}
76
113
126 if (!sfc) return;
127
130 .stream_id = stream_id,
131 .max_stream_data = initial_stream_window,
132 .stream_window_size = initial_stream_window,
133 };
134}
135
149
150 if (!fc || rtt_sample_us == 0) return;
151
152 if (!fc->rtt_initialized) {
154 fc->rttvar_us = rtt_sample_us / 2;
156 fc->rtt_initialized = true;
157 } else {
158 difference =
160 fc->rttvar_us = (3 * fc->rttvar_us + difference) / 4;
161 fc->srtt_us = (7 * fc->srtt_us + rtt_sample_us) / 8;
162 if (rtt_sample_us < fc->min_rtt_us) fc->min_rtt_us = rtt_sample_us;
163 }
164
166}
167
180 if (!fc || !fc->rtt_initialized) return QUIC_NET_QUALITY_NORMAL;
181
182 if (fc->srtt_us <= 50000)
184 else if (fc->srtt_us <= 150000)
186 else if (fc->srtt_us <= 500000)
188 else
190
191 return fc->quality;
192}
193
207 if (!fc || fc->quality < QUIC_NET_QUALITY_HIGH_RTT) return;
208
209 if (fc->conn_window_size < fc->conn_max_window) {
210 fc->conn_window_size *= 2;
212 }
213
214 if (sfc && sfc->stream_window_size < fc->stream_max_window) {
215 sfc->stream_window_size *= 2;
216 if (sfc->stream_window_size > fc->stream_max_window)
217 sfc->stream_window_size = fc->stream_max_window;
218 }
219}
220
231 if (fc) fc->conn_consumed = saturating_add(fc->conn_consumed, bytes);
232}
233
246 (void)fc;
247 if (sfc) sfc->stream_consumed = saturating_add(sfc->stream_consumed, bytes);
248}
249
269 size_t *written, bool force) {
271 size_t value_size;
272
273 if (written) *written = 0;
274 if (!fc || !buf || !written) return -1;
275 if (!force && !should_update(fc->conn_consumed, fc->max_data, fc->conn_window_size)) return 0;
276
277 maximum = saturating_add(fc->conn_consumed, fc->conn_window_size);
278 if (maximum < fc->max_data) maximum = fc->max_data;
279 value_size = varint_size(maximum);
280 if (buf_len < 1 + value_size) return -1;
281
282 buf[0] = QUIC_FRAME_MAX_DATA;
283 write_varint(buf + 1, maximum, value_size);
284 fc->max_data = maximum;
285 *written = 1 + value_size;
286 return 1;
287}
288
309 uint8_t *buf, size_t buf_len, size_t *written,
310 bool force) {
312 size_t id_size;
313 size_t value_size;
314
315 if (written) *written = 0;
316 if (!fc || !sfc || !buf || !written) return -1;
317 if (!force &&
318 !should_update(sfc->stream_consumed, sfc->max_stream_data, sfc->stream_window_size))
319 return 0;
320
321 maximum = saturating_add(sfc->stream_consumed, sfc->stream_window_size);
322 if (maximum < sfc->max_stream_data) maximum = sfc->max_stream_data;
323 id_size = varint_size(sfc->stream_id);
324 value_size = varint_size(maximum);
325 if (buf_len < 1 + id_size + value_size) return -1;
326
328 write_varint(buf + 1, sfc->stream_id, id_size);
329 write_varint(buf + 1 + id_size, maximum, value_size);
330 sfc->max_stream_data = maximum;
331 *written = 1 + id_size + value_size;
332 return 1;
333}
size_t cap
Definition otel.c:56
void quic_flow_control_consume_conn(quic_conn_fc_t *fc, uint64_t bytes)
Account for connection-level flow control bytes consumed by the peer.
#define QUIC_FRAME_MAX_DATA
void quic_flow_control_update_rtt(quic_conn_fc_t *fc, uint64_t rtt_sample_us)
Feed an RTT sample into the smoothed RTT estimator and re-evaluate quality.
void quic_flow_control_init(quic_conn_fc_t *fc, uint64_t initial_conn_window, uint64_t initial_stream_window, uint64_t max_conn_window, uint64_t max_stream_window)
Initialize the connection-level flow control state.
int quic_flow_control_maybe_send_max_stream_data(quic_conn_fc_t *fc, quic_stream_fc_t *sfc, uint8_t *buf, size_t buf_len, size_t *written, bool force)
Write a MAX_STREAM_DATA frame if the stream limit should be raised.
quic_net_quality_t quic_flow_control_evaluate_quality(quic_conn_fc_t *fc)
Recompute the network quality class from the smoothed RTT.
int quic_flow_control_maybe_send_max_data(quic_conn_fc_t *fc, uint8_t *buf, size_t buf_len, size_t *written, bool force)
Write a MAX_DATA frame if the connection limit should be raised.
void quic_flow_control_consume_stream(quic_conn_fc_t *fc, quic_stream_fc_t *sfc, uint64_t bytes)
Account for stream-level flow control bytes consumed by the peer.
void quic_stream_fc_init(quic_stream_fc_t *sfc, uint64_t stream_id, uint64_t initial_stream_window)
Initialize the flow control state for a single stream.
void quic_flow_control_adjust_windows(quic_conn_fc_t *fc, quic_stream_fc_t *sfc)
Grow flow control windows when the network quality is degraded.
#define QUIC_MAX_VARINT
#define QUIC_FRAME_MAX_STREAM_DATA
struct quic_conn_fc quic_conn_fc_t
quic_net_quality_t
@ QUIC_NET_QUALITY_NORMAL
@ QUIC_NET_QUALITY_POOR
@ QUIC_NET_QUALITY_HIGH_RTT
@ QUIC_NET_QUALITY_EXCELLENT
struct quic_stream_fc quic_stream_fc_t
#define QUIC_FLOW_CONTROL_MIN_INITIAL_WINDOW
uint64_t stream_max_window
uint64_t conn_window_size
quic_net_quality_t quality
uint64_t conn_consumed
uint64_t conn_max_window