2 * Copyright 2022-2023 The OpenSSL Project Authors. All Rights Reserved.
4 * Licensed under the Apache License 2.0 (the "License"). You may not use
5 * this file except in compliance with the License. You can obtain a copy
6 * in the file LICENSE in the source distribution or at
7 * https://www.openssl.org/source/license.html
10 #include "internal/quic_cfq.h"
11 #include "internal/numbers.h"
13 typedef struct quic_cfq_item_ex_st QUIC_CFQ_ITEM_EX
;
15 struct quic_cfq_item_ex_st
{
17 QUIC_CFQ_ITEM_EX
*prev
, *next
;
18 unsigned char *encoded
;
23 uint32_t priority
, pn_space
, flags
;
27 uint64_t ossl_quic_cfq_item_get_frame_type(const QUIC_CFQ_ITEM
*item
)
29 QUIC_CFQ_ITEM_EX
*ex
= (QUIC_CFQ_ITEM_EX
*)item
;
31 return ex
->frame_type
;
34 const unsigned char *ossl_quic_cfq_item_get_encoded(const QUIC_CFQ_ITEM
*item
)
36 QUIC_CFQ_ITEM_EX
*ex
= (QUIC_CFQ_ITEM_EX
*)item
;
41 size_t ossl_quic_cfq_item_get_encoded_len(const QUIC_CFQ_ITEM
*item
)
43 QUIC_CFQ_ITEM_EX
*ex
= (QUIC_CFQ_ITEM_EX
*)item
;
45 return ex
->encoded_len
;
48 int ossl_quic_cfq_item_get_state(const QUIC_CFQ_ITEM
*item
)
50 QUIC_CFQ_ITEM_EX
*ex
= (QUIC_CFQ_ITEM_EX
*)item
;
55 uint32_t ossl_quic_cfq_item_get_pn_space(const QUIC_CFQ_ITEM
*item
)
57 QUIC_CFQ_ITEM_EX
*ex
= (QUIC_CFQ_ITEM_EX
*)item
;
62 int ossl_quic_cfq_item_is_unreliable(const QUIC_CFQ_ITEM
*item
)
64 QUIC_CFQ_ITEM_EX
*ex
= (QUIC_CFQ_ITEM_EX
*)item
;
66 return (ex
->flags
& QUIC_CFQ_ITEM_FLAG_UNRELIABLE
) != 0;
69 typedef struct quic_cfq_item_list_st
{
70 QUIC_CFQ_ITEM_EX
*head
, *tail
;
75 * Invariant: A CFQ item is always in exactly one of these lists, never more
78 * Invariant: The list the CFQ item is determined exactly by the state field
81 QUIC_CFQ_ITEM_LIST new_list
, tx_list
, free_list
;
84 static int compare(const QUIC_CFQ_ITEM_EX
*a
, const QUIC_CFQ_ITEM_EX
*b
)
86 if (a
->pn_space
< b
->pn_space
)
88 else if (a
->pn_space
> b
->pn_space
)
91 if (a
->priority
> b
->priority
)
93 else if (a
->priority
< b
->priority
)
99 static void list_remove(QUIC_CFQ_ITEM_LIST
*l
, QUIC_CFQ_ITEM_EX
*n
)
106 n
->prev
->next
= n
->next
;
108 n
->next
->prev
= n
->prev
;
109 n
->prev
= n
->next
= NULL
;
112 static void list_insert_head(QUIC_CFQ_ITEM_LIST
*l
, QUIC_CFQ_ITEM_EX
*n
)
123 static void list_insert_tail(QUIC_CFQ_ITEM_LIST
*l
, QUIC_CFQ_ITEM_EX
*n
)
134 static void list_insert_after(QUIC_CFQ_ITEM_LIST
*l
,
135 QUIC_CFQ_ITEM_EX
*ref
,
140 if (ref
->next
!= NULL
)
147 static void list_insert_sorted(QUIC_CFQ_ITEM_LIST
*l
, QUIC_CFQ_ITEM_EX
*n
,
148 int (*cmp
)(const QUIC_CFQ_ITEM_EX
*a
,
149 const QUIC_CFQ_ITEM_EX
*b
))
151 QUIC_CFQ_ITEM_EX
*p
= l
->head
, *pprev
= NULL
;
154 l
->head
= l
->tail
= n
;
155 n
->prev
= n
->next
= NULL
;
159 for (; p
!= NULL
&& cmp(p
, n
) < 0; pprev
= p
, p
= p
->next
);
162 list_insert_tail(l
, n
);
163 else if (pprev
== NULL
)
164 list_insert_head(l
, n
);
166 list_insert_after(l
, pprev
, n
);
169 QUIC_CFQ
*ossl_quic_cfq_new(void)
171 QUIC_CFQ
*cfq
= OPENSSL_zalloc(sizeof(*cfq
));
179 static void clear_item(QUIC_CFQ_ITEM_EX
*item
)
181 if (item
->free_cb
!= NULL
) {
182 item
->free_cb(item
->encoded
, item
->encoded_len
, item
->free_cb_arg
);
184 item
->free_cb
= NULL
;
185 item
->encoded
= NULL
;
186 item
->encoded_len
= 0;
192 static void free_list_items(QUIC_CFQ_ITEM_LIST
*l
)
194 QUIC_CFQ_ITEM_EX
*p
, *pnext
;
196 for (p
= l
->head
; p
!= NULL
; p
= pnext
) {
203 void ossl_quic_cfq_free(QUIC_CFQ
*cfq
)
208 free_list_items(&cfq
->new_list
);
209 free_list_items(&cfq
->tx_list
);
210 free_list_items(&cfq
->free_list
);
214 static QUIC_CFQ_ITEM_EX
*cfq_get_free(QUIC_CFQ
*cfq
)
216 QUIC_CFQ_ITEM_EX
*item
= cfq
->free_list
.head
;
221 item
= OPENSSL_zalloc(sizeof(*item
));
226 list_insert_tail(&cfq
->free_list
, item
);
230 QUIC_CFQ_ITEM
*ossl_quic_cfq_add_frame(QUIC_CFQ
*cfq
,
235 const unsigned char *encoded
,
237 cfq_free_cb
*free_cb
,
240 QUIC_CFQ_ITEM_EX
*item
= cfq_get_free(cfq
);
245 item
->priority
= priority
;
246 item
->frame_type
= frame_type
;
247 item
->pn_space
= pn_space
;
248 item
->encoded
= (unsigned char *)encoded
;
249 item
->encoded_len
= encoded_len
;
250 item
->free_cb
= free_cb
;
251 item
->free_cb_arg
= free_cb_arg
;
253 item
->state
= QUIC_CFQ_STATE_NEW
;
255 list_remove(&cfq
->free_list
, item
);
256 list_insert_sorted(&cfq
->new_list
, item
, compare
);
257 return &item
->public;
260 void ossl_quic_cfq_mark_tx(QUIC_CFQ
*cfq
, QUIC_CFQ_ITEM
*item
)
262 QUIC_CFQ_ITEM_EX
*ex
= (QUIC_CFQ_ITEM_EX
*)item
;
265 case QUIC_CFQ_STATE_NEW
:
266 list_remove(&cfq
->new_list
, ex
);
267 list_insert_tail(&cfq
->tx_list
, ex
);
268 ex
->state
= QUIC_CFQ_STATE_TX
;
270 case QUIC_CFQ_STATE_TX
:
271 break; /* nothing to do */
273 assert(0); /* invalid state (e.g. in free state) */
278 void ossl_quic_cfq_mark_lost(QUIC_CFQ
*cfq
, QUIC_CFQ_ITEM
*item
,
281 QUIC_CFQ_ITEM_EX
*ex
= (QUIC_CFQ_ITEM_EX
*)item
;
283 if (ossl_quic_cfq_item_is_unreliable(item
)) {
284 ossl_quic_cfq_release(cfq
, item
);
289 case QUIC_CFQ_STATE_NEW
:
290 if (priority
!= UINT32_MAX
&& priority
!= ex
->priority
) {
291 list_remove(&cfq
->new_list
, ex
);
292 ex
->priority
= priority
;
293 list_insert_sorted(&cfq
->new_list
, ex
, compare
);
295 break; /* nothing to do */
296 case QUIC_CFQ_STATE_TX
:
297 if (priority
!= UINT32_MAX
)
298 ex
->priority
= priority
;
299 list_remove(&cfq
->tx_list
, ex
);
300 list_insert_sorted(&cfq
->new_list
, ex
, compare
);
301 ex
->state
= QUIC_CFQ_STATE_NEW
;
304 assert(0); /* invalid state (e.g. in free state) */
310 * Releases a CFQ item. The item may be in either state (NEW or TX) prior to the
311 * call. The QUIC_CFQ_ITEM pointer must not be used following this call.
313 void ossl_quic_cfq_release(QUIC_CFQ
*cfq
, QUIC_CFQ_ITEM
*item
)
315 QUIC_CFQ_ITEM_EX
*ex
= (QUIC_CFQ_ITEM_EX
*)item
;
318 case QUIC_CFQ_STATE_NEW
:
319 list_remove(&cfq
->new_list
, ex
);
320 list_insert_tail(&cfq
->free_list
, ex
);
323 case QUIC_CFQ_STATE_TX
:
324 list_remove(&cfq
->tx_list
, ex
);
325 list_insert_tail(&cfq
->free_list
, ex
);
329 assert(0); /* invalid state (e.g. in free state) */
334 QUIC_CFQ_ITEM
*ossl_quic_cfq_get_priority_head(const QUIC_CFQ
*cfq
,
337 QUIC_CFQ_ITEM_EX
*item
= cfq
->new_list
.head
;
339 for (; item
!= NULL
&& item
->pn_space
!= pn_space
; item
= item
->next
);
344 return &item
->public;
347 QUIC_CFQ_ITEM
*ossl_quic_cfq_item_get_priority_next(const QUIC_CFQ_ITEM
*item
,
350 QUIC_CFQ_ITEM_EX
*ex
= (QUIC_CFQ_ITEM_EX
*)item
;
357 for (; ex
!= NULL
&& ex
->pn_space
!= pn_space
; ex
= ex
->next
);
360 return NULL
; /* ubsan */