← Commit list

01eff554af

Stefan Eissing
Stefan Eissing
2026-09-18
SUCCESS Duration: 410s

Subject

u32_ptrset: a set of uint32_t + void* values

Binary

No binary (build failed)

Architecture & Compiler Flags

Architecture: x86_64-linux-gnu Compiler: gcc (Ubuntu 11.4.0-1ubuntu1~22.04.3) 11.4.0

—

Build Log

(log yok)

Commit Diff GitHub →

lib/Makefile.inc+2 -2
@@ -289,8 +289,8 @@ LIB_CFILES = \
thrdqueue.c \
transfer.c \
u8_strset.c \
+ u32_ptrset.c \
uint-bset.c \
- uint-hash.c \
uint-spbset.c \
uint-table.c \
url.c \
@@ -421,8 +421,8 @@ LIB_HFILES = \
thrdqueue.h \
transfer.h \
u8_strset.h \
+ u32_ptrset.h \
uint-bset.h \
- uint-hash.h \
uint-spbset.h \
uint-table.h \
url.h \
lib/http2.c+8 -8
@@ -28,7 +28,7 @@
#include "urldata.h"
#include "bufq.h"
-#include "uint-hash.h"
+#include "u32_ptrset.h"
#include "http1.h"
#include "http2.h"
#include "http.h"
@@ -97,7 +97,7 @@ struct cf_h2_ctx {
struct bufc_pool stream_bufcp; /* spares for stream buffers */
struct dynbuf scratch; /* scratch buffer for temp use */
- struct uint_hash streams; /* hash of `data->mid` to `h2_stream_ctx` */
+ struct u32_ptrset streams; /* hash of `data->mid` to `h2_stream_ctx` */
size_t drain_total; /* sum of all stream's UrlState drain */
uint32_t initial_win_size; /* current initial window size (settings) */
uint32_t max_concurrent_streams;
@@ -165,7 +165,7 @@ static void h2_stream_ctx_free(struct h2_stream_ctx *stream)
curlx_free(stream);
}
-static void h2_stream_hash_free(unsigned int id, void *stream)
+static void h2_stream_hash_free(uint32_t id, void *stream)
{
(void)id;
DEBUGASSERT(stream);
@@ -178,7 +178,7 @@ static void cf_h2_ctx_init(struct cf_h2_ctx *ctx, bool via_h1_upgrade)
Curl_bufq_initp(&ctx->inbufq, &ctx->stream_bufcp, H2_NW_RECV_CHUNKS, 0);
Curl_bufq_initp(&ctx->outbufq, &ctx->stream_bufcp, H2_NW_SEND_CHUNKS, 0);
curlx_dyn_init(&ctx->scratch, CURL_MAX_HTTP_HEADER);
- Curl_uint32_hash_init(&ctx->streams, 63, h2_stream_hash_free);
+ Curl_u32_ptrset_init(&ctx->streams, h2_stream_hash_free);
ctx->remote_max_sid = INT32_MAX;
ctx->via_h1_upgrade = via_h1_upgrade;
ctx->initialized = TRUE;
@@ -193,7 +193,7 @@ static void cf_h2_ctx_free(struct cf_h2_ctx *ctx)
Curl_bufq_free(&ctx->outbufq);
Curl_bufcp_free(&ctx->stream_bufcp);
curlx_dyn_free(&ctx->scratch);
- Curl_uint32_hash_destroy(&ctx->streams);
+ Curl_u32_ptrset_clear(&ctx->streams);
memset(ctx, 0, sizeof(*ctx));
}
curlx_free(ctx);
@@ -255,7 +255,7 @@ static CURLcode cf_h2_update_settings(struct cf_h2_ctx *ctx,
#define H2_STREAM_CTX(ctx, data) \
((struct h2_stream_ctx *)( \
- (data) ? Curl_uint32_hash_get(&(ctx)->streams, (data)->mid) : NULL))
+ (data) ? Curl_u32_ptrset_get(&(ctx)->streams, (data)->mid) : NULL))
static struct h2_stream_ctx *h2_stream_ctx_create(struct cf_h2_ctx *ctx)
{
@@ -381,7 +381,7 @@ static CURLcode http2_data_setup(struct Curl_cfilter *cf,
if(!stream)
return CURLE_OUT_OF_MEMORY;
- if(!Curl_uint32_hash_set(&ctx->streams, data->mid, stream)) {
+ if(Curl_u32_ptrset_set(&ctx->streams, data->mid, stream)) {
h2_stream_ctx_free(stream);
return CURLE_OUT_OF_MEMORY;
}
@@ -444,7 +444,7 @@ static void http2_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
}
}
- Curl_uint32_hash_remove(&ctx->streams, data->mid);
+ Curl_u32_ptrset_unset(&ctx->streams, data->mid);
}
static int h2_client_new(struct Curl_cfilter *cf,
lib/u32_ptrset.c+277 -0
@@ -0,0 +1,277 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <[email protected]>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "curl_setup.h"
+
+#include "u32_ptrset.h"
+
+/* random patterns for API verification */
+#ifdef DEBUGBUILD
+#define CURL_U32_PTRSET_MAGIC 0x75703272
+#endif
+
+#define CURL_U32_PTRSET_DEBUG 0
+
+#ifndef CURL_SWAP
+#define CURL_SWAP(a, b) (((a) ^= (b)), ((b) ^= (a)), ((a) ^= (b)))
+#endif
+
+#define CURL_U32_SLOT_CNT(bits) ((bits) ? (1U << (bits)) : 0)
+#define CURL_U32_PTRSET_SLOT_CNT(s) CURL_U32_SLOT_CNT((s)->slotbits)
+#define CURL_U32_PTRSET_SLOT_IDX(s, i) (uint32_t)((i) & (s)->slotmask)
+
+/* A hashset for tuples (id, ptr) using Robin Hood Hashing.
+ * <https://www.cs.cornell.edu/courses/JavaAndDS/files/hashing_RobinHood.pdf>
+ * The basic idea here to handle collisions by robbing "rich" entries and
+ * giving to the "poor":
+ * - We have an array: (id, ptr) are ideally placed at index "id % size".
+ * - If slot at index is already occupied, we have a collision.
+ * - A simple collision strategy would look at the next index, and the
+ * next until finding an empty slot.
+ * - The drawback is that this may lead to many checks on lookups, as it
+ * will need to also look at subsequent slots until it finds the match.
+ * The amount of lookups is the "probe sequence length" (psl) and this
+ * may vary greatly between entries.
+ * - Robin Hood Hashing balances the 'psl's of all entries more evenly:
+ * - psl == 0 means an entry is in exactly the right slot
+ * - psl == 1 means it is in the slot right after. psl == 2 is the slot
+ * after that, etc.
+ * - when inserting a new entry, track its psl. Finding a slot where
+ * the existing entry has a lower psl makes a swap. Put the new entry
+ * and its psl there, take the previous entry and its psl and find
+ * the next best slot for the previous entry. */
+void Curl_u32_ptrset_init(struct u32_ptrset *set,
+ Curl_u32_ptrset_dtor *dtor)
+{
+ memset(set, 0, sizeof(*set));
+ set->dtor = dtor;
+#ifdef DEBUGBUILD
+ set->init = CURL_U32_PTRSET_MAGIC;
+#endif
+}
+
+void Curl_u32_ptrset_clear(struct u32_ptrset *set)
+{
+ uint32_t i;
+ DEBUGASSERT(set->init == CURL_U32_PTRSET_MAGIC);
+ if(set->dtor)
+ for(i = 0; i < CURL_U32_PTRSET_SLOT_CNT(set); ++i)
+ if(set->data[i])
+ set->dtor(set->ids[i], set->data[i]);
+
+ curlx_safefree(set->data);
+ Curl_u32_ptrset_init(set, set->dtor);
+}
+
+static void u32_ptrset_add(struct u32_ptrset *set, uint32_t id, void *ptr)
+{
+ uint32_t i = CURL_U32_PTRSET_SLOT_IDX(set, id);
+ uint32_t psl = 0;
+ while(set->data[i]) {
+ if(psl > set->psl[i]) { /* SWAP */
+ void *tmpdata;
+#if CURL_U32_PTRSET_DEBUG
+ curl_mfprintf(stderr, "u32_ptrset_add %u=%p to idx=%u\n", id, ptr, i);
+#endif
+ tmpdata = set->data[i];
+ set->data[i] = ptr;
+ ptr = tmpdata;
+ CURL_SWAP(set->psl[i], psl);
+ CURL_SWAP(set->ids[i], id);
+ }
+ i = CURL_U32_PTRSET_SLOT_IDX(set, i + 1);
+ ++psl;
+ }
+ set->ids[i] = id;
+ set->data[i] = ptr;
+ set->psl[i] = psl;
+ ++set->count;
+#if CURL_U32_PTRSET_DEBUG
+ curl_mfprintf(stderr, "u32_ptrset_add %u=%p to idx=%u\n", id, ptr, i);
+#endif
+}
+
+static bool u32_ptrset_grow(struct u32_ptrset *set)
+{
+ uint32_t i, *prev_ids;
+ uint8_t nslotbits, pslotbits;
+ void **prev_data;
+ size_t nslots;
+ void *d;
+
+ if(set->slotbits >= 32)
+ return FALSE;
+ nslotbits = set->slotbits ? (uint8_t)(set->slotbits + 1) : 5;
+ nslots = CURL_U32_SLOT_CNT(nslotbits);
+#if CURL_U32_PTRSET_DEBUG
+ curl_mfprintf(stderr, "u32_ptrset_grow from bits=%d to %d, slots=%zu\n",
+ set->slotbits, nslotbits, nslots);
+#endif
+ d = curlx_calloc(1, (nslots * sizeof(void *)) +
+ (2 * nslots * sizeof(uint32_t)));
+ if(!d)
+ return FALSE;
+
+ prev_data = set->data;
+ prev_ids = set->ids;
+ pslotbits = set->slotbits;
+#if defined(__GNUC__) && __GNUC__ >= 13
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wanalyzer-allocation-size"
+#endif
+ set->data = d;
+ set->ids = (uint32_t *)(set->data + nslots);
+ set->psl = (uint32_t *)(set->ids + nslots);
+#if defined(__GNUC__) && __GNUC__ >= 13
+#pragma GCC diagnostic pop
+#endif
+
+ set->slotbits = nslotbits;
+ set->slotmask = (uint32_t)((1U << set->slotbits) - 1);
+ set->count = 0;
+ /* re-add previous entries */
+ for(i = 0; i < CURL_U32_SLOT_CNT(pslotbits); ++i) {
+ if(prev_data[i])
+ u32_ptrset_add(set, prev_ids[i], prev_data[i]);
+ }
+ if(prev_data)
+ curlx_free(prev_data);
+ return TRUE;
+}
+
+static bool u32_ptrset_get_index(struct u32_ptrset *set,
+ uint32_t id, uint32_t *pindex)
+{
+ uint32_t i = CURL_U32_PTRSET_SLOT_IDX(set, id);
+ uint32_t psl = 0;
+ if(!set->data)
+ return FALSE;
+ while(set->data[i] && (psl <= set->psl[i])) {
+ if(set->ids[i] == id) {
+ *pindex = i;
+#if CURL_U32_PTRSET_DEBUG
+ curl_mfprintf(stderr, "u32_ptrset_index %u=%p at idx=%u\n",
+ id, set->data[i], i);
+#endif
+ return TRUE;
+ }
+ i = CURL_U32_PTRSET_SLOT_IDX(set, i + 1);
+ ++psl;
+ }
+#if CURL_U32_PTRSET_DEBUG
+ curl_mfprintf(stderr, "u32_ptrset_index %u not found\n", id);
+#endif
+ *pindex = 0;
+ return FALSE;
+}
+
+uint32_t Curl_u32_ptrset_count(struct u32_ptrset *set)
+{
+ return set->count;
+}
+
+void *Curl_u32_ptrset_get(struct u32_ptrset *set, uint32_t id)
+{
+ uint32_t i;
+ DEBUGASSERT(set->init == CURL_U32_PTRSET_MAGIC);
+ if(u32_ptrset_get_index(set, id, &i))
+ return set->data[i];
+ return NULL;
+}
+
+CURLcode Curl_u32_ptrset_set(struct u32_ptrset *set,
+ uint32_t id, void *ptr)
+{
+ uint32_t i;
+
+ DEBUGASSERT(set->init == CURL_U32_PTRSET_MAGIC);
+ if(!ptr) {
+ Curl_u32_ptrset_unset(set, id);
+ return CURLE_OK;
+ }
+
+ if(u32_ptrset_get_index(set, id, &i)) {
+ /* `id` is in set, replace value */
+ if(set->dtor && (set->data[i] != ptr))
+ set->dtor(set->ids[i], set->data[i]);
+#if CURL_U32_PTRSET_DEBUG
+ curl_mfprintf(stderr, "u32_ptrset_set replace %u=%p at idx=%u\n",
+ id, ptr, i);
+#endif
+ set->data[i] = ptr;
+ return CURLE_OK;
+ }
+ /* `id` not in set yet, grow if full */
+ if((set->count >= CURL_U32_PTRSET_SLOT_CNT(set)) &&
+ !u32_ptrset_grow(set)) {
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ u32_ptrset_add(set, id, ptr);
+ return CURLE_OK;
+}
+
+static void u32_ptrset_unset(struct u32_ptrset *set, uint32_t id)
+{
+ uint32_t i, j;
+
+ DEBUGASSERT(set->init == CURL_U32_PTRSET_MAGIC);
+ if(u32_ptrset_get_index(set, id, &i)) {
+ /* `id` is in set */
+#if CURL_U32_PTRSET_DEBUG
+ curl_mfprintf(stderr, "u32_ptrset_unset %u at idx=%u\n", id, i);
+#endif
+ if(set->dtor)
+ set->dtor(set->ids[i], set->data[i]);
+ set->data[i] = NULL;
+ set->ids[i] = set->psl[i] = 0;
+ --set->count;
+ j = CURL_U32_PTRSET_SLOT_IDX(set, i + 1);
+ /* shift all entries with positive psl "down" */
+ while(set->data[j] && set->psl[j]) {
+ set->data[i] = set->data[j];
+ set->ids[i] = set->ids[j];
+ set->psl[i] = set->psl[j] - 1;
+ set->data[j] = NULL;
+ set->ids[j] = set->psl[j] = 0;
+ i = j;
+ j = CURL_U32_PTRSET_SLOT_IDX(set, i + 1);
+ }
+ }
+}
+
+void Curl_u32_ptrset_unset(struct u32_ptrset *set, uint32_t id)
+{
+ u32_ptrset_unset(set, id);
+}
+
+void Curl_u32_ptrset_visit(struct u32_ptrset *set,
+ Curl_u32_ptrset_visit_cb *cb,
+ void *user_data)
+{
+ uint32_t i;
+ for(i = 0; i < CURL_U32_PTRSET_SLOT_CNT(set); ++i) {
+ if(set->data[i] && !cb(set->ids[i], set->data[i], user_data))
+ break;
+ }
+}
lib/u32_ptrset.h+70 -0
@@ -0,0 +1,70 @@
+#ifndef HEADER_CURL_U32_PTRSET_H
+#define HEADER_CURL_U32_PTRSET_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <[email protected]>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "curl_setup.h"
+
+typedef void Curl_u32_ptrset_dtor(uint32_t id, void *ptr);
+
+/* A set that can hold up UINT32_MAX pointers identified by an `id'.
+ * Setting a pointer for an existing id replaces the previous one.
+ * Getting the pointer for an id not in the set returns NULL.
+ * Setting an id to NULL unsets the id.
+ */
+struct u32_ptrset {
+ void **data; /* #slots array of pointers */
+ uint32_t *ids; /* #slots array of `id` values */
+ uint32_t *psl; /* #slots array of "probe sequence length" values */
+ uint32_t slotmask;
+ uint32_t count;
+ Curl_u32_ptrset_dtor *dtor;
+ uint8_t slotbits;
+#ifdef DEBUGBUILD
+ int32_t init;
+#endif
+};
+
+void Curl_u32_ptrset_init(struct u32_ptrset *set,
+ Curl_u32_ptrset_dtor *dtor);
+void Curl_u32_ptrset_clear(struct u32_ptrset *set);
+
+uint32_t Curl_u32_ptrset_count(struct u32_ptrset *set);
+
+void *Curl_u32_ptrset_get(struct u32_ptrset *set, uint32_t id);
+
+/* Set pointer for id. */
+CURLcode Curl_u32_ptrset_set(struct u32_ptrset *set,
+ uint32_t id, void *ptr);
+
+void Curl_u32_ptrset_unset(struct u32_ptrset *set, uint32_t id);
+
+typedef bool Curl_u32_ptrset_visit_cb(uint32_t id, void *ptr,
+ void *user_data);
+
+/* Visit all entries in the set until the callback returns FALSE */
+void Curl_u32_ptrset_visit(struct u32_ptrset *set,
+ Curl_u32_ptrset_visit_cb *cb,
+ void *user_data);
+
+#endif /* HEADER_CURL_U32_PTRSET_H */
lib/uint-hash.c+0 -235
@@ -1,235 +0,0 @@
-/***************************************************************************
- * _ _ ____ _
- * Project ___| | | | _ \| |
- * / __| | | | |_) | |
- * | (__| |_| | _ <| |___
- * \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <[email protected]>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-#include "curl_setup.h"
-
-#include "uint-hash.h"
-
-/* random patterns for API verification */
-#ifdef DEBUGBUILD
-#define CURL_UINT32_HASHINIT 0x7117e779
-#endif
-
-static uint32_t uint32_hash_hash(uint32_t id, uint32_t slots)
-{
- return (id % slots);
-}
-
-struct uint_hash_entry {
- struct uint_hash_entry *next;
- void *value;
- uint32_t id;
-};
-
-void Curl_uint32_hash_init(struct uint_hash *h,
- uint32_t slots,
- Curl_uint32_hash_dtor *dtor)
-{
- DEBUGASSERT(h);
- DEBUGASSERT(slots);
-
- h->table = NULL;
- h->dtor = dtor;
- h->size = 0;
- h->slots = slots;
-#ifdef DEBUGBUILD
- h->init = CURL_UINT32_HASHINIT;
-#endif
-}
-
-static struct uint_hash_entry *uint32_hash_mk_entry(uint32_t id, void *value)
-{
- struct uint_hash_entry *e;
-
- /* allocate the struct for the hash entry */
- e = curlx_malloc(sizeof(*e));
- if(e) {
- e->id = id;
- e->next = NULL;
- e->value = value;
- }
- return e;
-}
-
-static void uint32_hash_entry_clear(struct uint_hash *h,
- struct uint_hash_entry *e)
-{
- DEBUGASSERT(h);
- DEBUGASSERT(e);
- if(e->value) {
- if(h->dtor)
- h->dtor(e->id, e->value);
- e->value = NULL;
- }
-}
-
-static void uint32_hash_entry_destroy(struct uint_hash *h,
- struct uint_hash_entry *e)
-{
- uint32_hash_entry_clear(h, e);
- curlx_free(e);
-}
-
-static void uint32_hash_entry_unlink(struct uint_hash *h,
- struct uint_hash_entry **he_anchor,
- struct uint_hash_entry *he)
-{
- *he_anchor = he->next;
- --h->size;
-}
-
-static void uint32_hash_elem_link(struct uint_hash *h,
- struct uint_hash_entry **he_anchor,
- struct uint_hash_entry *he)
-{
- he->next = *he_anchor;
- *he_anchor = he;
- ++h->size;
-}
-
-#define CURL_UINT32_HASH_SLOT(h, id) h->table[uint32_hash_hash(id, (h)->slots)]
-#define CURL_UINT32_HASH_SLOT_ADDR(h, id) &CURL_UINT32_HASH_SLOT(h, id)
-
-bool Curl_uint32_hash_set(struct uint_hash *h, uint32_t id, void *value)
-{
- struct uint_hash_entry *he, **slot;
-
- DEBUGASSERT(h);
- DEBUGASSERT(h->slots);
- DEBUGASSERT(h->init == CURL_UINT32_HASHINIT);
- if(!h->table) {
- h->table = curlx_calloc(h->slots, sizeof(*he));
- if(!h->table)
- return FALSE; /* OOM */
- }
-
- slot = CURL_UINT32_HASH_SLOT_ADDR(h, id);
- for(he = *slot; he; he = he->next) {
- if(he->id == id) {
- /* existing key entry, overwrite by clearing old pointer */
- uint32_hash_entry_clear(h, he);
- he->value = value;
- return TRUE;
- }
- }
-
- he = uint32_hash_mk_entry(id, value);
- if(!he)
- return FALSE; /* OOM */
-
- uint32_hash_elem_link(h, slot, he);
- return TRUE;
-}
-
-bool Curl_uint32_hash_remove(struct uint_hash *h, uint32_t id)
-{
- DEBUGASSERT(h);
- DEBUGASSERT(h->slots);
- DEBUGASSERT(h->init == CURL_UINT32_HASHINIT);
- if(h->table) {
- struct uint_hash_entry *he, **he_anchor;
-
- he_anchor = CURL_UINT32_HASH_SLOT_ADDR(h, id);
- while(*he_anchor) {
- he = *he_anchor;
- if(id == he->id) {
- uint32_hash_entry_unlink(h, he_anchor, he);
- uint32_hash_entry_destroy(h, he);
- return TRUE;
- }
- he_anchor = &he->next;
- }
- }
- return FALSE;
-}
-
-void *Curl_uint32_hash_get(struct uint_hash *h, uint32_t id)
-{
- DEBUGASSERT(h);
- DEBUGASSERT(h->init == CURL_UINT32_HASHINIT);
- if(h->table) {
- struct uint_hash_entry *he;
- DEBUGASSERT(h->slots);
- he = CURL_UINT32_HASH_SLOT(h, id);
- while(he) {
- if(id == he->id) {
- return he->value;
- }
- he = he->next;
- }
- }
- return NULL;
-}
-
-/* @unittest 1616 */
-UNITTEST void uint_hash_clear(struct uint_hash *h);
-UNITTEST void uint_hash_clear(struct uint_hash *h)
-{
- if(h && h->table) {
- struct uint_hash_entry *he, **he_anchor;
- size_t i;
- DEBUGASSERT(h->init == CURL_UINT32_HASHINIT);
- for(i = 0; i < h->slots; ++i) {
- he_anchor = &h->table[i];
- while(*he_anchor) {
- he = *he_anchor;
- uint32_hash_entry_unlink(h, he_anchor, he);
- uint32_hash_entry_destroy(h, he);
- }
- }
- }
-}
-
-void Curl_uint32_hash_destroy(struct uint_hash *h)
-{
- DEBUGASSERT(h->init == CURL_UINT32_HASHINIT);
- if(h->table) {
- uint_hash_clear(h);
- curlx_safefree(h->table);
- }
- DEBUGASSERT(h->size == 0);
- h->slots = 0;
-}
-
-uint32_t Curl_uint32_hash_count(struct uint_hash *h)
-{
- DEBUGASSERT(h->init == CURL_UINT32_HASHINIT);
- return h->size;
-}
-
-void Curl_uint32_hash_visit(struct uint_hash *h,
- Curl_uint32_hash_visit_cb *cb,
- void *user_data)
-{
- if(h && h->table && cb) {
- struct uint_hash_entry *he;
- size_t i;
- DEBUGASSERT(h->init == CURL_UINT32_HASHINIT);
- for(i = 0; i < h->slots; ++i) {
- for(he = h->table[i]; he; he = he->next) {
- if(!cb(he->id, he->value, user_data))
- return;
- }
- }
- }
-}
lib/uint-hash.h+0 -59
@@ -1,59 +0,0 @@
-#ifndef HEADER_CURL_UINT_HASH_H
-#define HEADER_CURL_UINT_HASH_H
-/***************************************************************************
- * _ _ ____ _
- * Project ___| | | | _ \| |
- * / __| | | | |_) | |
- * | (__| |_| | _ <| |___
- * \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <[email protected]>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-#include "curl_setup.h"
-
-/* A version with uint32_t as key */
-typedef void Curl_uint32_hash_dtor(uint32_t id, void *value);
-struct uint_hash_entry;
-
-/* Hash for `uint32_t` as key */
-struct uint_hash {
- struct uint_hash_entry **table;
- Curl_uint32_hash_dtor *dtor;
- uint32_t slots;
- uint32_t size;
-#ifdef DEBUGBUILD
- int init;
-#endif
-};
-
-void Curl_uint32_hash_init(struct uint_hash *h,
- uint32_t slots,
- Curl_uint32_hash_dtor *dtor);
-void Curl_uint32_hash_destroy(struct uint_hash *h);
-bool Curl_uint32_hash_set(struct uint_hash *h, uint32_t id, void *value);
-bool Curl_uint32_hash_remove(struct uint_hash *h, uint32_t id);
-void *Curl_uint32_hash_get(struct uint_hash *h, uint32_t id);
-uint32_t Curl_uint32_hash_count(struct uint_hash *h);
-
-typedef bool Curl_uint32_hash_visit_cb(uint32_t id, void *value,
- void *user_data);
-
-void Curl_uint32_hash_visit(struct uint_hash *h,
- Curl_uint32_hash_visit_cb *cb,
- void *user_data);
-
-#endif /* HEADER_CURL_UINT_HASH_H */
lib/vquic/cf-ngtcp2-cmn.c+10 -10
@@ -49,7 +49,7 @@
#include "urldata.h"
#include "url.h"
-#include "uint-hash.h"
+#include "u32_ptrset.h"
#include "curl_trc.h"
#include "rand.h"
#include "multiif.h"
@@ -90,7 +90,7 @@ void Curl_cf_ngtcp2_h3_stream_ctx_free(struct h3_stream_ctx *stream)
curlx_free(stream);
}
-static void h3_stream_hash_free(unsigned int id, void *stream)
+static void h3_stream_set_free(uint32_t id, void *stream)
{
(void)id;
DEBUGASSERT(stream);
@@ -129,7 +129,7 @@ CURLcode Curl_cf_ngtcp2_ctx_init(struct cf_ngtcp2_ctx *ctx,
Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE,
H3_STREAM_POOL_SPARES);
curlx_dyn_init(&ctx->scratch, CURL_MAX_HTTP_HEADER);
- Curl_uint32_hash_init(&ctx->streams, 63, h3_stream_hash_free);
+ Curl_u32_ptrset_init(&ctx->streams, h3_stream_set_free);
ctx->init_h3_conn_cb = init_h3_conn_cb;
ctx->initialized = TRUE;
return Curl_vquic_tls_peer_init(origin, peer, sslc, &ctx->ssl_peer);
@@ -142,7 +142,7 @@ void Curl_cf_ngtcp2_ctx_cleanup(struct cf_ngtcp2_ctx *ctx)
Curl_vquic_ctx_free(&ctx->q);
Curl_bufcp_free(&ctx->stream_bufcp);
curlx_dyn_free(&ctx->scratch);
- Curl_uint32_hash_destroy(&ctx->streams);
+ Curl_u32_ptrset_clear(&ctx->streams);
Curl_ssl_peer_cleanup(&ctx->ssl_peer);
curlx_safefree(ctx->tunnel_inbuf);
ctx->tunnel_inbuf_len = 0;
@@ -1399,7 +1399,7 @@ static struct h3_stream_ctx *cf_ngtcp2_get_stream(struct cf_ngtcp2_ctx *ctx,
struct cf_ngtcp2_sfind_ctx fctx;
fctx.stream_id = stream_id;
fctx.stream = NULL;
- Curl_uint32_hash_visit(&ctx->streams, cf_ngtcp2_sfind, &fctx);
+ Curl_u32_ptrset_visit(&ctx->streams, cf_ngtcp2_sfind, &fctx);
return fctx.stream;
}
#else
@@ -1857,7 +1857,7 @@ static void cf_ngtcp2_setup_keep_alive(struct Curl_cfilter *cf,
ngtcp2_conn_set_keep_alive_timeout(ctx->qconn, UINT64_MAX);
CURL_TRC_CF(data, cf, "no peer idle timeout, unset keep-alive");
}
- else if(!Curl_uint32_hash_count(&ctx->streams)) {
+ else if(!Curl_u32_ptrset_count(&ctx->streams)) {
ngtcp2_conn_set_keep_alive_timeout(ctx->qconn, UINT64_MAX);
CURL_TRC_CF(data, cf, "no active streams, unset keep-alive");
}
@@ -1900,12 +1900,12 @@ CURLcode Curl_cf_ngtcp2_h3_stream_setup(struct Curl_cfilter *cf,
stream->window_size_max = H3_STREAM_WINDOW_SIZE_INITIAL;
Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
- if(!Curl_uint32_hash_set(&ctx->streams, data->mid, stream)) {
+ if(Curl_u32_ptrset_set(&ctx->streams, data->mid, stream)) {
Curl_cf_ngtcp2_h3_stream_ctx_free(stream);
return CURLE_OUT_OF_MEMORY;
}
- if(Curl_uint32_hash_count(&ctx->streams) == 1)
+ if(Curl_u32_ptrset_count(&ctx->streams) == 1)
cf_ngtcp2_setup_keep_alive(cf, data);
return CURLE_OK;
@@ -1942,8 +1942,8 @@ void Curl_cf_ngtcp2_h3_stream_done(struct Curl_cfilter *cf,
if(stream) {
CURL_TRC_CF(data, cf, "[%" PRId64 "] easy handle is done", stream->id);
Curl_cf_ngtcp2_h3_stream_close(cf, data, stream);
- Curl_uint32_hash_remove(&ctx->streams, data->mid);
- if(!Curl_uint32_hash_count(&ctx->streams))
+ Curl_u32_ptrset_unset(&ctx->streams, data->mid);
+ if(!Curl_u32_ptrset_count(&ctx->streams))
cf_ngtcp2_setup_keep_alive(cf, data);
}
}
lib/vquic/cf-ngtcp2-cmn.h+2 -2
@@ -58,7 +58,7 @@
#include <nghttp3/nghttp3.h>
#include "http1.h"
-#include "uint-hash.h"
+#include "u32_ptrset.h"
#include "vtls/vtls.h"
#include "vquic/vquic_int.h"
#include "vquic/vquic-tls.h"
@@ -128,7 +128,7 @@ struct cf_ngtcp2_ctx {
struct curltime handshake_at; /* time connect handshake finished */
struct bufc_pool stream_bufcp; /* chunk pool for streams */
struct dynbuf scratch; /* temp buffer for header construction */
- struct uint_hash streams; /* hash data->mid to h3_stream_ctx */
+ struct u32_ptrset streams; /* set of data->mid + h3_stream_ctx */
uint64_t used_bidi_streams; /* bidi streams we have opened */
uint64_t max_bidi_streams; /* max bidi streams we can open */
size_t earlydata_max; /* max amount of early data supported by
lib/vquic/cf-ngtcp2.c+1 -1
@@ -27,7 +27,7 @@
#include "urldata.h"
#include "url.h"
-#include "uint-hash.h"
+#include "u32_ptrset.h"
#include "curl_trc.h"
#include "rand.h"
#include "multiif.h"
lib/vquic/cf-quiche.c+10 -10
@@ -29,7 +29,7 @@
#include <openssl/ssl.h>
#include "bufq.h"
-#include "uint-hash.h"
+#include "u32_ptrset.h"
#include "urldata.h"
#include "cfilters.h"
#include "cf-socket.h"
@@ -86,7 +86,7 @@ struct cf_quiche_ctx {
uint8_t scid[QUICHE_MAX_CONN_ID_LEN];
struct curltime started_at; /* time the current attempt started */
struct curltime handshake_at; /* time connect handshake finished */
- struct uint_hash streams; /* hash `data->mid` to `stream_ctx` */
+ struct u32_ptrset streams; /* set of `data->mid` + `stream_ctx` */
struct dynbuf h1hdr; /* temp buffer for header construction */
struct bufq writebuf; /* temp buffer for writing bodies */
curl_off_t data_recvd;
@@ -107,7 +107,7 @@ static void quiche_debug_log(const char *line, void *argp)
}
#endif
-static void h3_stream_hash_free(unsigned int id, void *stream);
+static void h3_stream_set_free(uint32_t id, void *stream);
static CURLcode cf_quiche_ctx_init(struct cf_quiche_ctx *ctx,
struct Curl_peer *origin,
@@ -122,7 +122,7 @@ static CURLcode cf_quiche_ctx_init(struct cf_quiche_ctx *ctx,
}
#endif
curlx_dyn_init(&ctx->h1hdr, CURL_MAX_HTTP_HEADER);
- Curl_uint32_hash_init(&ctx->streams, 63, h3_stream_hash_free);
+ Curl_u32_ptrset_init(&ctx->streams, h3_stream_set_free);
Curl_bufq_init2(&ctx->writebuf, H3_STREAM_CHUNK_SIZE, H3_STREAM_RECV_CHUNKS,
BUFQ_OPT_SOFT_LIMIT);
ctx->data_recvd = 0;
@@ -138,7 +138,7 @@ static void cf_quiche_ctx_free(struct cf_quiche_ctx *ctx)
Curl_vquic_tls_cleanup(&ctx->tls);
Curl_ssl_peer_cleanup(&ctx->ssl_peer);
Curl_vquic_ctx_free(&ctx->q);
- Curl_uint32_hash_destroy(&ctx->streams);
+ Curl_u32_ptrset_clear(&ctx->streams);
curlx_dyn_free(&ctx->h1hdr);
Curl_bufq_free(&ctx->writebuf);
}
@@ -193,7 +193,7 @@ static void h3_stream_ctx_free(struct h3_stream_ctx *stream)
curlx_free(stream);
}
-static void h3_stream_hash_free(unsigned int id, void *stream)
+static void h3_stream_set_free(uint32_t id, void *stream)
{
(void)id;
DEBUGASSERT(stream);
@@ -233,7 +233,7 @@ static void cf_quiche_for_all_streams(struct Curl_cfilter *cf,
vctx.multi = multi;
vctx.cb = do_cb;
vctx.user_data = user_data;
- Curl_uint32_hash_visit(&ctx->streams, cf_quiche_stream_do, &vctx);
+ Curl_u32_ptrset_visit(&ctx->streams, cf_quiche_stream_do, &vctx);
}
static bool cf_quiche_do_resume(struct Curl_cfilter *cf,
@@ -278,7 +278,7 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf,
stream->id = -1;
Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
- if(!Curl_uint32_hash_set(&ctx->streams, data->mid, stream)) {
+ if(Curl_u32_ptrset_set(&ctx->streams, data->mid, stream)) {
h3_stream_ctx_free(stream);
return CURLE_OUT_OF_MEMORY;
}
@@ -318,7 +318,7 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
if(stream) {
CURL_TRC_CF(data, cf, "[%" PRIu64 "] easy handle is done", stream->id);
cf_quiche_stream_close(cf, data, stream);
- Curl_uint32_hash_remove(&ctx->streams, data->mid);
+ Curl_u32_ptrset_unset(&ctx->streams, data->mid);
}
}
@@ -635,7 +635,7 @@ static CURLcode cf_poll_events(struct Curl_cfilter *cf,
dctx.cf = cf;
dctx.multi = data->multi;
dctx.ev = ev;
- Curl_uint32_hash_visit(&ctx->streams, cf_quiche_disp_event, &dctx);
+ Curl_u32_ptrset_visit(&ctx->streams, cf_quiche_disp_event, &dctx);
quiche_h3_event_free(ev);
}
}
lib/vquic/vquic_int.h+2 -1
@@ -28,6 +28,7 @@
#ifdef USE_HTTP3
#include "bufq.h"
+#include "u32_ptrset.h"
#define MAX_UDP_PAYLOAD_SIZE 1452
@@ -78,7 +79,7 @@ struct cf_quic_ctx {
};
#define H3_STREAM_CTX(ctx, data) \
- ((data) ? Curl_uint32_hash_get(&(ctx)->streams, (data)->mid) : NULL)
+ ((data) ? Curl_u32_ptrset_get(&(ctx)->streams, (data)->mid) : NULL)
CURLcode Curl_vquic_ctx_init(struct Curl_easy *data,
struct cf_quic_ctx *qctx);
tests/data/Makefile.am+0 -1
@@ -1634,7 +1634,6 @@ TESTCASES = \
test1613 \
test1614 \
test1615 \
- test1616 \
test1617 \
test1618 \
test1619 \
tests/data/test1616+0 -19
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="US-ASCII"?>
-<testcase>
-<info>
-<keywords>
-unittest
-uint_hash
-</keywords>
-</info>
-
-# Client-side
-<client>
-<features>
-unittest
-</features>
-<name>
-uint_hash create/add/destroy testing, exercising clean functions
-</name>
-</client>
-</testcase>
tests/data/test3211+2 -1
@@ -5,6 +5,7 @@
unittest
uint_bset
u8_strset
+u32_ptrset
</keywords>
</info>
@@ -14,7 +15,7 @@ u8_strset
unittest
</features>
<name>
-uint_bset and u8_strset unit tests
+uint_bset, u8_strset and u32_ptrset unit tests
</name>
</client>
</testcase>
tests/unit/Makefile.inc+0 -1
@@ -55,7 +55,6 @@ TESTS_C = \
unit1612.c \
unit1614.c \
unit1615.c \
- unit1616.c \
unit1620.c \
unit1625.c \
unit1626.c \
tests/unit/unit1399.c+1 -1
@@ -120,7 +120,7 @@ static CURLcode test_unit1399(const char *arg)
/* Accumulated times are now 3 seconds:
* - 2 after 1st request start
- * - 1 aftger 2nd requerst start */
+ * - 1 after 2nd requerst start */
expect_timer_seconds(data, 3);
UNITTEST_END(t1399_stop(data))
tests/unit/unit1616.c+0 -85
@@ -1,85 +0,0 @@
-/***************************************************************************
- * _ _ ____ _
- * Project ___| | | | _ \| |
- * / __| | | | |_) | |
- * | (__| |_| | _ <| |___
- * \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <[email protected]>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-#include "unitcheck.h"
-#include "uint-hash.h"
-
-static void t1616_mydtor(uint32_t id, void *elem)
-{
- int *ptr = (int *)elem;
- (void)id;
- curlx_free(ptr);
-}
-
-static CURLcode t1616_setup(struct uint_hash *hash)
-{
- Curl_uint32_hash_init(hash, 15, t1616_mydtor);
- return CURLE_OK;
-}
-
-static void t1616_stop(struct uint_hash *hash)
-{
- Curl_uint32_hash_destroy(hash);
-}
-
-static CURLcode test_unit1616(const char *arg)
-{
- struct uint_hash hash;
-
- UNITTEST_BEGIN(t1616_setup(&hash))
-
- int *value, *v;
- int *value2;
- bool ok;
-
- uint32_t key = 20;
- uint32_t key2 = 25;
-
- value = curlx_malloc(sizeof(int));
- abort_unless(value, "Out of memory");
- *value = 199;
- ok = Curl_uint32_hash_set(&hash, key, value);
- if(!ok)
- curlx_free(value);
- abort_unless(ok, "insertion into hash failed");
- v = Curl_uint32_hash_get(&hash, key);
- abort_unless(v == value, "lookup present entry failed");
- v = Curl_uint32_hash_get(&hash, key2);
- abort_unless(!v, "lookup missing entry failed");
- uint_hash_clear(&hash);
-
- /* Attempt to add another key/value pair */
- value2 = curlx_malloc(sizeof(int));
- abort_unless(value2, "Out of memory");
- *value2 = 204;
- ok = Curl_uint32_hash_set(&hash, key2, value2);
- if(!ok)
- curlx_free(value2);
- abort_unless(ok, "insertion into hash failed");
- v = Curl_uint32_hash_get(&hash, key2);
- abort_unless(v == value2, "lookup present entry failed");
- v = Curl_uint32_hash_get(&hash, key);
- abort_unless(!v, "lookup missing entry failed");
-
- UNITTEST_END(t1616_stop(&hash))
-}
tests/unit/unit3211.c+59 -1
@@ -25,6 +25,7 @@
#include "urldata.h"
#include "uint-bset.h"
#include "u8_strset.h"
+#include "u32_ptrset.h"
#include "curl_trc.h"
static void t3211_check_bset(const char *name, uint32_t capacity,
@@ -202,7 +203,7 @@ static void t3211_check_u8_strset(void)
fail_unless(!t3211_strcmp(buf, Curl_u8_strset_get(&set, idx)),
"wrong get loop5");
}
- fail_unless(Curl_u8_strset_count(&set) == 9, "wrong count aftger add 5");
+ fail_unless(Curl_u8_strset_count(&set) == 9, "wrong count after add 5");
Curl_u8_strset_clear(&set);
@@ -220,6 +221,62 @@ static void t3211_check_u8_strset(void)
fail_unless(!Curl_u8_strset_count(&set), "cleared strset not empty");
}
+static void t3211_check_u32_ptrset(void)
+{
+ struct u32_ptrset set;
+ CURLcode result;
+ uint32_t i, idx;
+
+ Curl_u32_ptrset_init(&set, NULL);
+ fail_unless(!Curl_u32_ptrset_count(&set), "initial ptrset not empty");
+
+ result = Curl_u32_ptrset_set(&set, 0, CURL_UNCONST("123"));
+ fail_unless(!result, "ptrset add1 failed");
+ fail_unless(Curl_u32_ptrset_get(&set, 0), "ptrset get failed");
+ fail_unless(!t3211_strcmp("123", Curl_u32_ptrset_get(&set, 0)),
+ "ptrset wrong get1");
+ result = Curl_u32_ptrset_set(&set, 0, CURL_UNCONST("456"));
+ fail_unless(!result, "add2 failed");
+ fail_unless(!t3211_strcmp("456", Curl_u32_ptrset_get(&set, 0)),
+ "ptrset wrong get2");
+ Curl_u32_ptrset_unset(&set, 0);
+ fail_unless(!Curl_u32_ptrset_get(&set, 0), "ptrset unset failed");
+
+ /* Initial size is 64, add 64 hash collisions */
+ for(i = 0; i < 64; ++i) {
+ idx = (uint32_t)((64 * i) + 3);
+ result = Curl_u32_ptrset_set(&set, idx, CURL_UNCONST("loop64"));
+ fail_unless(!result, "ptrset loop64 add failed");
+ fail_unless(!t3211_strcmp("loop64", Curl_u32_ptrset_get(&set, idx)),
+ "ptrset wrong get loop64");
+ }
+
+ /* Remove collided entry 2, check again */
+ idx = (uint32_t)((64 * 2) + 3);
+ Curl_u32_ptrset_unset(&set, idx);
+ fail_unless(!Curl_u32_ptrset_get(&set, idx), "ptrset unset2 failed");
+
+ /* Add entry 2 again, check */
+ idx = (uint8_t)((64 * 2) + 3);
+ result = Curl_u32_ptrset_set(&set, idx, CURL_UNCONST("loop64b"));
+ fail_unless(!result, "ptrset re-add 2 failed");
+ fail_unless(!t3211_strcmp("loop64b", Curl_u32_ptrset_get(&set, idx)),
+ "ptrset wrong re-add 2 get");
+
+ /* Add a 65th, set grows */
+ fail_unless(Curl_u32_ptrset_count(&set) == 64,
+ "ptrset wrong count pre add 5");
+ idx = (uint32_t)((65 * 4) + 3);
+ result = Curl_u32_ptrset_set(&set, idx, CURL_UNCONST("add65"));
+ fail_unless(!result, "ptrset add65 failed");
+ fail_unless(!t3211_strcmp("add65", Curl_u32_ptrset_get(&set, idx)),
+ "ptrset wrong result after add65");
+ fail_unless(Curl_u32_ptrset_count(&set) == 65, "wrong count after add65");
+
+ Curl_u32_ptrset_clear(&set);
+ fail_unless(!Curl_u32_ptrset_count(&set), "ptrset cleared not empty");
+}
+
static CURLcode test_unit3211(const char *arg)
{
UNITTEST_BEGIN_SIMPLE
@@ -244,6 +301,7 @@ static CURLcode test_unit3211(const char *arg)
t3211_check_bset("s2", 1000, s2, CURL_ARRAYSIZE(s2));
t3211_check_u8_strset();
+ t3211_check_u32_ptrset();
UNITTEST_END_SIMPLE
}