blob: 2bb6e69469171306757fcac64eea59b656c01ebe [file]
/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <assert.h>
#include <stddef.h>
#include <apr_strings.h>
#include <httpd.h>
#include <http_core.h>
#include <http_connection.h>
#include <http_log.h>
#include "h2_private.h"
#include "h2_io.h"
#include "h2_io_set.h"
#define h2_io_IDX(list, i) ((h2_io**)(list)->elts)[i]
struct h2_io_set {
apr_array_header_t *list;
};
h2_io_set *h2_io_set_create(apr_pool_t *pool)
{
h2_io_set *sp = apr_pcalloc(pool, sizeof(h2_io_set));
if (sp) {
sp->list = apr_array_make(pool, 100, sizeof(h2_io*));
if (!sp->list) {
return NULL;
}
}
return sp;
}
void h2_io_set_destroy(h2_io_set *sp)
{
int i;
for (i = 0; i < sp->list->nelts; ++i) {
h2_io *io = h2_io_IDX(sp->list, i);
h2_io_destroy(io);
}
sp->list->nelts = 0;
}
static int h2_stream_id_cmp(const void *s1, const void *s2)
{
h2_io **pio1 = (h2_io **)s1;
h2_io **pio2 = (h2_io **)s2;
return (*pio1)->id - (*pio2)->id;
}
h2_io *h2_io_set_get(h2_io_set *sp, int stream_id)
{
/* we keep the array sorted by id, so lookup can be done
* by bsearch.
*/
h2_io **ps;
h2_io key;
h2_io *pkey = &key;
memset(&key, 0, sizeof(key));
key.id = stream_id;
ps = bsearch(&pkey, sp->list->elts, sp->list->nelts,
sp->list->elt_size, h2_stream_id_cmp);
return ps? *ps : NULL;
}
static void h2_io_set_sort(h2_io_set *sp)
{
qsort(sp->list->elts, sp->list->nelts, sp->list->elt_size,
h2_stream_id_cmp);
}
apr_status_t h2_io_set_add(h2_io_set *sp, h2_io *io)
{
h2_io *existing = h2_io_set_get(sp, io->id);
if (!existing) {
int last;
APR_ARRAY_PUSH(sp->list, h2_io*) = io;
/* Normally, streams get added in ascending order if id. We
* keep the array sorted, so we just need to check of the newly
* appended stream has a lower id than the last one. if not,
* sorting is not necessary.
*/
last = sp->list->nelts - 1;
if (last > 0
&& (h2_io_IDX(sp->list, last)->id
< h2_io_IDX(sp->list, last-1)->id)) {
h2_io_set_sort(sp);
}
}
return APR_SUCCESS;
}
static void remove_idx(h2_io_set *sp, int idx)
{
int n;
--sp->list->nelts;
n = sp->list->nelts - idx;
if (n > 0) {
/* Close the hole in the array by moving the upper
* parts down one step.
*/
h2_io **selts = (h2_io**)sp->list->elts;
memmove(selts + idx, selts + idx + 1, n * sizeof(h2_io*));
}
}
h2_io *h2_io_set_remove(h2_io_set *sp, h2_io *io)
{
int i;
for (i = 0; i < sp->list->nelts; ++i) {
h2_io *e = h2_io_IDX(sp->list, i);
if (e == io) {
remove_idx(sp, i);
return e;
}
}
return NULL;
}
h2_io *h2_io_set_pop_highest_prio(h2_io_set *set)
{
/* For now, this just removes the first element in the set.
* the name is misleading...
*/
if (set->list->nelts > 0) {
h2_io *io = h2_io_IDX(set->list, 0);
remove_idx(set, 0);
return io;
}
return NULL;
}
int h2_io_set_is_empty(h2_io_set *sp)
{
AP_DEBUG_ASSERT(sp);
return sp->list->nelts == 0;
}
int h2_io_set_iter(h2_io_set *sp,
h2_io_set_iter_fn *iter, void *ctx)
{
int i;
for (i = 0; i < sp->list->nelts; ++i) {
h2_io *s = h2_io_IDX(sp->list, i);
if (!iter(ctx, s)) {
return 0;
}
}
return 1;
}
apr_size_t h2_io_set_size(h2_io_set *sp)
{
return sp->list->nelts;
}