/*
* Copyright 2022 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#include "internal/quic_stream_map.h"
#include "internal/nelem.h"
/*
* QUIC Stream Map
* ===============
*/
DEFINE_LHASH_OF_EX(QUIC_STREAM);
static void shutdown_flush_done(QUIC_STREAM_MAP *qsm, QUIC_STREAM *qs);
/* Circular list management. */
static void list_insert_tail(QUIC_STREAM_LIST_NODE *l,
QUIC_STREAM_LIST_NODE *n)
{
/* Must not be in list. */
assert(n->prev == NULL && n->next == NULL
&& l->prev != NULL && l->next != NULL);
n->prev = l->prev;
n->prev->next = n;
l->prev = n;
n->next = l;
}
static void list_remove(QUIC_STREAM_LIST_NODE *l,
QUIC_STREAM_LIST_NODE *n)
{
assert(n->prev != NULL && n->next != NULL
&& n->prev != n && n->next != n);
n->prev->next = n->next;
n->next->prev = n->prev;
n->next = n->prev = NULL;
}
static QUIC_STREAM *list_next(QUIC_STREAM_LIST_NODE *l, QUIC_STREAM_LIST_NODE *n,
size_t off)
{
assert(n->prev != NULL && n->next != NULL
&& (n == l || (n->prev != n && n->next != n))
&& l->prev != NULL && l->next != NULL);
n = n->next;
if (n == l)
n = n->next;
if (n == l)
return NULL;
assert(n != NULL);
return (QUIC_STREAM *)(((char *)n) - off);
}
#define active_next(l, s) list_next((l), &(s)->active_node, \
offsetof(QUIC_STREAM, active_node))
#define accept_next(l, s) list_next((l), &(s)->accept_node, \
offsetof(QUIC_STREAM, accept_node))
#define ready_for_gc_next(l, s) list_next((l), &(s)->ready_for_gc_node, \
offsetof(QUIC_STREAM, ready_for_gc_node))
#define accept_head(l) list_next((l), (l), \
offsetof(QUIC_STREAM, accept_node))
#define ready_for_gc_head(l) list_next((l), (l), \
offsetof(QUIC_STREAM, ready_for_gc_node))
static unsigned long hash_stream(const QUIC_STREAM *s)
{
return (unsigned long)s->id;
}
static int cmp_stream(const QUIC_STREAM *a, const QUIC_STREAM *b)
{
if (a->id < b->id)
return -1;
if (a->id > b->id)
return 1;
return 0;
}
int ossl_quic_stream_map_init(QUIC_STREAM_MAP *qsm,
uint64_t (*get_stream_limit_cb)(int uni, void *arg),
void *get_stream_limit_cb_arg,
QUIC_RXFC *max_streams_bidi_rxfc,
QUIC_RXFC *max_streams_uni_rxfc,
int is_server)
{
qsm->map = lh_QUIC_STREAM_new(hash_stream, cmp_stream);
qsm->active_list.prev = qsm->active_list.next = &qsm->active_list;
qsm->accept_list.prev = qsm->accept_list.next = &qsm->accept_list;
qsm->ready_for_gc_list.prev = qsm->ready_for_gc_list.next
= &qsm->ready_for_gc_list;
qsm->rr_stepping = 1;
qsm->rr_counter = 0;
qsm->rr_cur = NULL;
qsm->num_accept = 0;
qsm->num_shutdown_flush = 0;
qsm->get_stream_limit_cb = get_stream_limit_cb;
qsm->get_stream_limit_cb_arg = get_stream_limit_cb_arg;
qsm->max_streams_bidi_rxfc = max_streams_bidi_rxfc;
qsm->max_streams_uni_rxfc = max_streams_uni_rxfc;
qsm->is_server = is_server;
return 1;
}
static void release_each(QUIC_STREAM *stream, void *arg)
{
QUIC_STREAM_MAP *qsm = arg;
ossl_quic_stream_map_release(qsm, stream);
}
void ossl_quic_stream_map_cleanup(QUIC_STREAM_MAP *qsm)
{
ossl_quic_stream_map_visit(qsm, release_each, qsm);
lh_QUIC_STREAM_free(qsm->map);
qsm->map = NULL;
}
void ossl_quic_stream_map_visit(QUIC_STREAM_MAP *qsm,
void (*visit_cb)(QUIC_STREAM *stream, void *arg),
void *visit_cb_arg)
{
lh_QUIC_STREAM_doall_arg(qsm->map, visit_cb, visit_cb_arg);
}
QUIC_STREAM *ossl_quic_stream_map_alloc(QUIC_STREAM_MAP *qsm,
uint64_t stream_id,
int type)
{
QUIC_STREAM *s;
QUIC_STREAM key;
key.id = stream_id;
s = lh_QUIC_STREAM_retrieve(qsm->map, &key);