lib: add meta_hash to connection, eliminate hash_offt

With a meta_hash at each connection (similar to easy handle, let
multi_ev.c store its pollsets as meta data, no longer needing its own
hashes.

This eliminates the last use of Curl_hash_offt. Remove it.

Closes #17095
This commit is contained in:
Stefan Eissing 2025-04-18 11:03:29 +02:00 committed by Daniel Stenberg
parent 1d66a769d7
commit 657aae79c0
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
18 changed files with 126 additions and 343 deletions

View file

@ -149,40 +149,3 @@ Called repeatedly, it iterates over all the entries in the hash table.
Note: it only guarantees functionality if the hash table remains untouched Note: it only guarantees functionality if the hash table remains untouched
during its iteration. during its iteration.
# `curl_off_t` dedicated hash functions
## `Curl_hash_offt_init`
~~~c
void Curl_hash_offt_init(struct Curl_hash *h,
size_t slots,
Curl_hash_dtor dtor);
~~~
Initializes a hash table for `curl_off_t` values. Pass in desired number of
`slots` and `dtor` function.
## `Curl_hash_offt_set`
~~~c
void *Curl_hash_offt_set(struct Curl_hash *h, curl_off_t id, void *elem);
~~~
Associate a custom `elem` pointer with the given `id`.
## `Curl_hash_offt_remove`
~~~c
int Curl_hash_offt_remove(struct Curl_hash *h, curl_off_t id);
~~~
Remove the `id` from the hash.
## `Curl_hash_offt_get`
~~~c
void *Curl_hash_offt_get(struct Curl_hash *h, curl_off_t id);
~~~
Get the pointer associated with the specified `id`.

View file

@ -126,6 +126,3 @@ Iterating a sparse bitset works the same as for bitset and table.
At last, there are places in libcurl such as the HTTP/2 and HTTP/3 protocol implementations that need At last, there are places in libcurl such as the HTTP/2 and HTTP/3 protocol implementations that need
to store their own data related to a transfer. `uint_hash` allows then to associate an unsigned int, to store their own data related to a transfer. `uint_hash` allows then to associate an unsigned int,
e.g. the transfer's `mid`, to their own data. e.g. the transfer's `mid`, to their own data.
This is just a variation of `hash_offt` that can associate data with a `connection_id`. Which
is a specialization of the generic `Curl_hash`.

View file

@ -164,7 +164,6 @@ LIB_CFILES = \
getinfo.c \ getinfo.c \
gopher.c \ gopher.c \
hash.c \ hash.c \
hash_offt.c \
headers.c \ headers.c \
hmac.c \ hmac.c \
hostip.c \ hostip.c \
@ -237,6 +236,7 @@ LIB_CFILES = \
timeval.c \ timeval.c \
transfer.c \ transfer.c \
uint-bset.c \ uint-bset.c \
uint-hash.c \
uint-spbset.c \ uint-spbset.c \
uint-table.c \ uint-table.c \
url.c \ url.c \
@ -314,7 +314,6 @@ LIB_HFILES = \
getinfo.h \ getinfo.h \
gopher.h \ gopher.h \
hash.h \ hash.h \
hash_offt.h \
headers.h \ headers.h \
hostip.h \ hostip.h \
hsts.h \ hsts.h \
@ -380,6 +379,7 @@ LIB_HFILES = \
timeval.h \ timeval.h \
transfer.h \ transfer.h \
uint-bset.h \ uint-bset.h \
uint-hash.h \
uint-spbset.h \ uint-spbset.h \
uint-table.h \ uint-table.h \
url.h \ url.h \

View file

@ -249,7 +249,7 @@ static void doh_probe_done(struct Curl_easy *data,
Curl_dyn_len(&doh_req->resp_body)); Curl_dyn_len(&doh_req->resp_body));
Curl_dyn_free(&doh_req->resp_body); Curl_dyn_free(&doh_req->resp_body);
} }
Curl_meta_clear(doh, CURL_EZM_DOH_PROBE); Curl_meta_remove(doh, CURL_EZM_DOH_PROBE);
} }
if(result) if(result)

View file

@ -1419,7 +1419,7 @@ CURLcode Curl_meta_set(struct Curl_easy *data, const char *key,
return CURLE_OK; return CURLE_OK;
} }
void Curl_meta_clear(struct Curl_easy *data, const char *key) void Curl_meta_remove(struct Curl_easy *data, const char *key)
{ {
Curl_hash_delete(&data->meta_hash, CURL_UNCONST(key), strlen(key) + 1); Curl_hash_delete(&data->meta_hash, CURL_UNCONST(key), strlen(key) + 1);
} }

View file

@ -29,7 +29,7 @@
#include <nghttp2/nghttp2.h> #include <nghttp2/nghttp2.h>
#include "urldata.h" #include "urldata.h"
#include "bufq.h" #include "bufq.h"
#include "hash_offt.h" #include "uint-hash.h"
#include "http1.h" #include "http1.h"
#include "http2.h" #include "http2.h"
#include "http.h" #include "http.h"

View file

@ -27,6 +27,7 @@
#include <curl/curl.h> #include <curl/curl.h>
#include "urldata.h" #include "urldata.h"
#include "url.h"
#include "cfilters.h" #include "cfilters.h"
#include "curl_trc.h" #include "curl_trc.h"
#include "multiif.h" #include "multiif.h"
@ -57,7 +58,7 @@ static void mev_in_callback(struct Curl_multi *multi, bool value)
*/ */
struct mev_sh_entry { struct mev_sh_entry {
struct uint_spbset xfers; /* bitset of transfers `mid`s on this socket */ struct uint_spbset xfers; /* bitset of transfers `mid`s on this socket */
struct Curl_hash_offt conns; /* hash of connections using this socket */ struct connectdata *conn; /* connection using this socket or NULL */
void *user_data; /* libcurl app data via curl_multi_assign() */ void *user_data; /* libcurl app data via curl_multi_assign() */
unsigned int action; /* CURL_POLL_IN/CURL_POLL_OUT we last told the unsigned int action; /* CURL_POLL_IN/CURL_POLL_OUT we last told the
* libcurl application to watch out for */ * libcurl application to watch out for */
@ -84,7 +85,6 @@ static void mev_sh_entry_dtor(void *freethis)
{ {
struct mev_sh_entry *entry = (struct mev_sh_entry *)freethis; struct mev_sh_entry *entry = (struct mev_sh_entry *)freethis;
Curl_uint_spbset_destroy(&entry->xfers); Curl_uint_spbset_destroy(&entry->xfers);
Curl_hash_offt_destroy(&entry->conns);
free(entry); free(entry);
} }
@ -117,7 +117,6 @@ mev_sh_entry_add(struct Curl_hash *sh, curl_socket_t s)
return NULL; /* major failure */ return NULL; /* major failure */
Curl_uint_spbset_init(&check->xfers); Curl_uint_spbset_init(&check->xfers);
Curl_hash_offt_init(&check->conns, CURL_MEV_CONN_HASH_SIZE, NULL);
/* make/add new hash entry */ /* make/add new hash entry */
if(!Curl_hash_add(sh, (char *)&s, sizeof(curl_socket_t), check)) { if(!Curl_hash_add(sh, (char *)&s, sizeof(curl_socket_t), check)) {
@ -136,7 +135,7 @@ static void mev_sh_entry_kill(struct Curl_multi *multi, curl_socket_t s)
static size_t mev_sh_entry_user_count(struct mev_sh_entry *e) static size_t mev_sh_entry_user_count(struct mev_sh_entry *e)
{ {
return Curl_uint_spbset_count(&e->xfers) + Curl_hash_offt_count(&e->conns); return Curl_uint_spbset_count(&e->xfers) + (e->conn ? 1 : 0);
} }
static bool mev_sh_entry_xfer_known(struct mev_sh_entry *e, static bool mev_sh_entry_xfer_known(struct mev_sh_entry *e,
@ -148,7 +147,7 @@ static bool mev_sh_entry_xfer_known(struct mev_sh_entry *e,
static bool mev_sh_entry_conn_known(struct mev_sh_entry *e, static bool mev_sh_entry_conn_known(struct mev_sh_entry *e,
struct connectdata *conn) struct connectdata *conn)
{ {
return !!Curl_hash_offt_get(&e->conns, conn->connection_id); return (e->conn == conn);
} }
static bool mev_sh_entry_xfer_add(struct mev_sh_entry *e, static bool mev_sh_entry_xfer_add(struct mev_sh_entry *e,
@ -164,7 +163,11 @@ static bool mev_sh_entry_conn_add(struct mev_sh_entry *e,
{ {
/* detect weird values */ /* detect weird values */
DEBUGASSERT(mev_sh_entry_user_count(e) < 100000); DEBUGASSERT(mev_sh_entry_user_count(e) < 100000);
return !!Curl_hash_offt_set(&e->conns, conn->connection_id, conn); DEBUGASSERT(!e->conn);
if(e->conn)
return FALSE;
e->conn = conn;
return TRUE;
} }
@ -180,7 +183,12 @@ static bool mev_sh_entry_xfer_remove(struct mev_sh_entry *e,
static bool mev_sh_entry_conn_remove(struct mev_sh_entry *e, static bool mev_sh_entry_conn_remove(struct mev_sh_entry *e,
struct connectdata *conn) struct connectdata *conn)
{ {
return Curl_hash_offt_remove(&e->conns, conn->connection_id); DEBUGASSERT(e->conn == conn);
if(e->conn == conn) {
e->conn = NULL;
return TRUE;
}
return FALSE;
} }
/* Purge any information about socket `s`. /* Purge any information about socket `s`.
@ -344,11 +352,11 @@ static CURLMcode mev_pollset_diff(struct Curl_multi *multi,
return CURLM_OUT_OF_MEMORY; return CURLM_OUT_OF_MEMORY;
} }
CURL_TRC_M(data, "ev entry fd=%" FMT_SOCKET_T ", added %s #%" FMT_OFF_T CURL_TRC_M(data, "ev entry fd=%" FMT_SOCKET_T ", added %s #%" FMT_OFF_T
", total=%u/%zu (xfer/conn)", s, ", total=%u/%d (xfer/conn)", s,
conn ? "connection" : "transfer", conn ? "connection" : "transfer",
conn ? conn->connection_id : data->mid, conn ? conn->connection_id : data->mid,
Curl_uint_spbset_count(&entry->xfers), Curl_uint_spbset_count(&entry->xfers),
Curl_hash_offt_count(&entry->conns)); entry->conn ? 1 : 0);
} }
else { else {
for(j = 0; j < prev_ps->num; j++) { for(j = 0; j < prev_ps->num; j++) {
@ -414,9 +422,9 @@ static CURLMcode mev_pollset_diff(struct Curl_multi *multi,
if(mresult) if(mresult)
return mresult; return mresult;
CURL_TRC_M(data, "ev entry fd=%" FMT_SOCKET_T ", removed transfer, " CURL_TRC_M(data, "ev entry fd=%" FMT_SOCKET_T ", removed transfer, "
"total=%u/%zu (xfer/conn)", s, "total=%u/%d (xfer/conn)", s,
Curl_uint_spbset_count(&entry->xfers), Curl_uint_spbset_count(&entry->xfers),
Curl_hash_offt_count(&entry->conns)); entry->conn ? 1 : 0);
} }
else { else {
mresult = mev_forget_socket(multi, data, s, "last user gone"); mresult = mev_forget_socket(multi, data, s, "last user gone");
@ -430,15 +438,22 @@ static CURLMcode mev_pollset_diff(struct Curl_multi *multi,
return CURLM_OK; return CURLM_OK;
} }
static void mev_pollset_dtor(void *key, size_t klen, void *entry)
{
(void)key;
(void)klen;
free(entry);
}
static struct easy_pollset* static struct easy_pollset*
mev_add_new_conn_pollset(struct Curl_hash_offt *h, curl_off_t id) mev_add_new_conn_pollset(struct connectdata *conn)
{ {
struct easy_pollset *ps; struct easy_pollset *ps;
ps = calloc(1, sizeof(*ps)); ps = calloc(1, sizeof(*ps));
if(!ps) if(!ps)
return NULL; return NULL;
if(!Curl_hash_offt_set(h, id, ps)) { if(Curl_conn_meta_set(conn, CURL_META_MEV_POLLSET, ps, mev_pollset_dtor)) {
free(ps); free(ps);
return NULL; return NULL;
} }
@ -446,14 +461,14 @@ mev_add_new_conn_pollset(struct Curl_hash_offt *h, curl_off_t id)
} }
static struct easy_pollset* static struct easy_pollset*
mev_add_new_xfer_pollset(struct uint_hash *h, unsigned int id) mev_add_new_xfer_pollset(struct Curl_easy *data)
{ {
struct easy_pollset *ps; struct easy_pollset *ps;
ps = calloc(1, sizeof(*ps)); ps = calloc(1, sizeof(*ps));
if(!ps) if(!ps)
return NULL; return NULL;
if(!Curl_uint_hash_set(h, id, ps)) { if(Curl_meta_set(data, CURL_META_MEV_POLLSET, ps, mev_pollset_dtor)) {
free(ps); free(ps);
return NULL; return NULL;
} }
@ -461,16 +476,14 @@ mev_add_new_xfer_pollset(struct uint_hash *h, unsigned int id)
} }
static struct easy_pollset * static struct easy_pollset *
mev_get_last_pollset(struct Curl_multi *multi, mev_get_last_pollset(struct Curl_easy *data,
struct Curl_easy *data,
struct connectdata *conn) struct connectdata *conn)
{ {
if(data) { if(data) {
if(conn) if(conn)
return Curl_hash_offt_get(&multi->ev.conn_pollsets, return Curl_conn_meta_get(conn, CURL_META_MEV_POLLSET);
conn->connection_id);
else if(data) else if(data)
return Curl_uint_hash_get(&multi->ev.xfer_pollsets, data->mid); return Curl_meta_get(data, CURL_META_MEV_POLLSET);
} }
return NULL; return NULL;
} }
@ -494,15 +507,13 @@ static CURLMcode mev_assess(struct Curl_multi *multi,
struct easy_pollset ps, *last_ps; struct easy_pollset ps, *last_ps;
mev_init_cur_pollset(&ps, data, conn); mev_init_cur_pollset(&ps, data, conn);
last_ps = mev_get_last_pollset(multi, data, conn); last_ps = mev_get_last_pollset(data, conn);
if(!last_ps && ps.num) { if(!last_ps && ps.num) {
if(conn) if(conn)
last_ps = mev_add_new_conn_pollset(&multi->ev.conn_pollsets, last_ps = mev_add_new_conn_pollset(conn);
conn->connection_id);
else else
last_ps = mev_add_new_xfer_pollset(&multi->ev.xfer_pollsets, last_ps = mev_add_new_xfer_pollset(data);
data->mid);
if(!last_ps) if(!last_ps)
return CURLM_OUT_OF_MEMORY; return CURLM_OUT_OF_MEMORY;
} }
@ -588,7 +599,7 @@ void Curl_multi_ev_expire_xfers(struct Curl_multi *multi,
while(Curl_uint_spbset_next(&entry->xfers, mid, &mid)); while(Curl_uint_spbset_next(&entry->xfers, mid, &mid));
} }
if(Curl_hash_offt_count(&entry->conns)) if(entry->conn)
*run_cpool = TRUE; *run_cpool = TRUE;
} }
} }
@ -605,7 +616,7 @@ void Curl_multi_ev_xfer_done(struct Curl_multi *multi,
DEBUGASSERT(!data->conn); /* transfer should have been detached */ DEBUGASSERT(!data->conn); /* transfer should have been detached */
if(data != multi->admin) { if(data != multi->admin) {
(void)mev_assess(multi, data, NULL); (void)mev_assess(multi, data, NULL);
Curl_uint_hash_remove(&multi->ev.xfer_pollsets, data->mid); Curl_meta_remove(data, CURL_META_MEV_POLLSET);
} }
} }
@ -614,36 +625,18 @@ void Curl_multi_ev_conn_done(struct Curl_multi *multi,
struct connectdata *conn) struct connectdata *conn)
{ {
(void)mev_assess(multi, data, conn); (void)mev_assess(multi, data, conn);
Curl_hash_offt_remove(&multi->ev.conn_pollsets, conn->connection_id); Curl_conn_meta_remove(conn, CURL_META_MEV_POLLSET);
} }
#define CURL_MEV_PS_HASH_SLOTS (991) /* nice prime */ #define CURL_MEV_PS_HASH_SLOTS (991) /* nice prime */
static void mev_hash_conn_pollset_free(curl_off_t id, void *entry)
{
(void)id;
free(entry);
}
static void mev_hash_xfer_pollset_free(unsigned int id, void *entry)
{
(void)id;
free(entry);
}
void Curl_multi_ev_init(struct Curl_multi *multi, size_t hashsize) void Curl_multi_ev_init(struct Curl_multi *multi, size_t hashsize)
{ {
Curl_hash_init(&multi->ev.sh_entries, hashsize, mev_sh_entry_hash, Curl_hash_init(&multi->ev.sh_entries, hashsize, mev_sh_entry_hash,
mev_sh_entry_compare, mev_sh_entry_dtor); mev_sh_entry_compare, mev_sh_entry_dtor);
Curl_uint_hash_init(&multi->ev.xfer_pollsets,
CURL_MEV_PS_HASH_SLOTS, mev_hash_xfer_pollset_free);
Curl_hash_offt_init(&multi->ev.conn_pollsets,
CURL_MEV_PS_HASH_SLOTS, mev_hash_conn_pollset_free);
} }
void Curl_multi_ev_cleanup(struct Curl_multi *multi) void Curl_multi_ev_cleanup(struct Curl_multi *multi)
{ {
Curl_hash_destroy(&multi->ev.sh_entries); Curl_hash_destroy(&multi->ev.sh_entries);
Curl_uint_hash_destroy(&multi->ev.xfer_pollsets);
Curl_hash_offt_destroy(&multi->ev.conn_pollsets);
} }

View file

@ -25,17 +25,17 @@
***************************************************************************/ ***************************************************************************/
#include "hash.h" #include "hash.h"
#include "hash_offt.h"
struct Curl_easy; struct Curl_easy;
struct Curl_multi; struct Curl_multi;
struct easy_pollset; struct easy_pollset;
struct uint_bset; struct uint_bset;
/* meta key for event pollset at easy handle or connection */
#define CURL_META_MEV_POLLSET "meta:mev:ps"
struct curl_multi_ev { struct curl_multi_ev {
struct Curl_hash sh_entries; struct Curl_hash sh_entries;
struct uint_hash xfer_pollsets;
struct Curl_hash_offt conn_pollsets;
}; };
/* Setup/teardown of multi event book-keeping. */ /* Setup/teardown of multi event book-keeping. */

View file

@ -26,207 +26,12 @@
#include <curl/curl.h> #include <curl/curl.h>
#include "hash_offt.h" #include "uint-hash.h"
#include "curl_memory.h" #include "curl_memory.h"
/* The last #include file should be: */ /* The last #include file should be: */
#include "memdebug.h" #include "memdebug.h"
/* random patterns for API verification */
#ifdef DEBUGBUILD
#define CURL_HASHOFFTINIT 0x7117e781
#endif
static size_t hash_offt_hash(curl_off_t id, size_t slots)
{
return (size_t)((id >= 0) ? (id % slots) : (-id % slots));
}
struct Curl_hash_offt_entry {
curl_off_t id;
struct Curl_hash_offt_entry *next;
void *value;
};
void Curl_hash_offt_init(struct Curl_hash_offt *h,
size_t slots,
Curl_hash_offt_dtor *dtor)
{
DEBUGASSERT(h);
DEBUGASSERT(slots);
h->table = NULL;
h->dtor = dtor;
h->size = 0;
h->slots = slots;
#ifdef DEBUGBUILD
h->init = CURL_HASHOFFTINIT;
#endif
}
static struct Curl_hash_offt_entry *
hash_offt_mk_entry(curl_off_t id, void *value)
{
struct Curl_hash_offt_entry *e;
/* allocate the struct for the hash entry */
e = malloc(sizeof(*e));
if(e) {
e->id = id;
e->next = NULL;
e->value = value;
}
return e;
}
static void hash_offt_entry_clear(struct Curl_hash_offt *h,
struct Curl_hash_offt_entry *e)
{
DEBUGASSERT(h);
DEBUGASSERT(e);
if(e->value) {
if(h->dtor)
h->dtor(e->id, e->value);
e->value = NULL;
}
}
static void hash_offt_entry_destroy(struct Curl_hash_offt *h,
struct Curl_hash_offt_entry *e)
{
hash_offt_entry_clear(h, e);
free(e);
}
static void hash_offt_entry_unlink(struct Curl_hash_offt *h,
struct Curl_hash_offt_entry **he_anchor,
struct Curl_hash_offt_entry *he)
{
*he_anchor = he->next;
--h->size;
}
static void hash_offtr_elem_link(struct Curl_hash_offt *h,
struct Curl_hash_offt_entry **he_anchor,
struct Curl_hash_offt_entry *he)
{
he->next = *he_anchor;
*he_anchor = he;
++h->size;
}
#define CURL_HASH_OFFT_SLOT(h,id) h->table[hash_offt_hash(id, h->slots)]
#define CURL_HASH_OFFT_SLOT_ADDR(h,id) &CURL_HASH_OFFT_SLOT(h,id)
bool Curl_hash_offt_set(struct Curl_hash_offt *h, curl_off_t id, void *value)
{
struct Curl_hash_offt_entry *he, **slot;
DEBUGASSERT(h);
DEBUGASSERT(h->slots);
DEBUGASSERT(h->init == CURL_HASHOFFTINIT);
if(!h->table) {
h->table = calloc(h->slots, sizeof(*he));
if(!h->table)
return FALSE; /* OOM */
}
slot = CURL_HASH_OFFT_SLOT_ADDR(h, id);
for(he = *slot; he; he = he->next) {
if(he->id == id) {
/* existing key entry, overwrite by clearing old pointer */
hash_offt_entry_clear(h, he);
he->value = value;
return TRUE;
}
}
he = hash_offt_mk_entry(id, value);
if(!he)
return FALSE; /* OOM */
hash_offtr_elem_link(h, slot, he);
return TRUE;
}
bool Curl_hash_offt_remove(struct Curl_hash_offt *h, curl_off_t id)
{
DEBUGASSERT(h);
DEBUGASSERT(h->slots);
DEBUGASSERT(h->init == CURL_HASHOFFTINIT);
if(h->table) {
struct Curl_hash_offt_entry *he, **he_anchor;
he_anchor = CURL_HASH_OFFT_SLOT_ADDR(h, id);
while(*he_anchor) {
he = *he_anchor;
if(id == he->id) {
hash_offt_entry_unlink(h, he_anchor, he);
hash_offt_entry_destroy(h, he);
return TRUE;
}
he_anchor = &he->next;
}
}
return FALSE;
}
void *Curl_hash_offt_get(struct Curl_hash_offt *h, curl_off_t id)
{
DEBUGASSERT(h);
DEBUGASSERT(h->init == CURL_HASHOFFTINIT);
if(h->table) {
struct Curl_hash_offt_entry *he;
DEBUGASSERT(h->slots);
he = CURL_HASH_OFFT_SLOT(h, id);
while(he) {
if(id == he->id) {
return he->value;
}
he = he->next;
}
}
return NULL;
}
void Curl_hash_offt_clear(struct Curl_hash_offt *h)
{
if(h && h->table) {
struct Curl_hash_offt_entry *he, **he_anchor;
size_t i;
DEBUGASSERT(h->init == CURL_HASHOFFTINIT);
for(i = 0; i < h->slots; ++i) {
he_anchor = &h->table[i];
while(*he_anchor) {
he = *he_anchor;
hash_offt_entry_unlink(h, he_anchor, he);
hash_offt_entry_destroy(h, he);
}
}
}
}
void
Curl_hash_offt_destroy(struct Curl_hash_offt *h)
{
DEBUGASSERT(h->init == CURL_HASHOFFTINIT);
if(h->table) {
Curl_hash_offt_clear(h);
Curl_safefree(h->table);
}
DEBUGASSERT(h->size == 0);
h->slots = 0;
}
size_t Curl_hash_offt_count(struct Curl_hash_offt *h)
{
DEBUGASSERT(h->init == CURL_HASHOFFTINIT);
return h->size;
}
/* FOR NOW: basically a duplicate of Curl_hash_offt, BUT we hope to
* eliminate the offt variant in the near future. */
/* random patterns for API verification */ /* random patterns for API verification */
#ifdef DEBUGBUILD #ifdef DEBUGBUILD
#define CURL_UINTHASHINIT 0x7117e779 #define CURL_UINTHASHINIT 0x7117e779
@ -311,7 +116,7 @@ static void uint_hash_elem_link(struct uint_hash *h,
} }
#define CURL_UINT_HASH_SLOT(h,id) h->table[uint_hash_hash(id, h->slots)] #define CURL_UINT_HASH_SLOT(h,id) h->table[uint_hash_hash(id, h->slots)]
#define CURL_UINT_HASH_SLOT_ADDR(h,id) &CURL_HASH_OFFT_SLOT(h,id) #define CURL_UINT_HASH_SLOT_ADDR(h,id) &CURL_UINT_HASH_SLOT(h,id)
bool Curl_uint_hash_set(struct uint_hash *h, unsigned int id, void *value) bool Curl_uint_hash_set(struct uint_hash *h, unsigned int id, void *value)
{ {
@ -401,8 +206,12 @@ static void uint_hash_clear(struct uint_hash *h)
} }
} }
void void Curl_uint_hash_clear(struct uint_hash *h)
Curl_uint_hash_destroy(struct uint_hash *h) {
uint_hash_clear(h);
}
void Curl_uint_hash_destroy(struct uint_hash *h)
{ {
DEBUGASSERT(h->init == CURL_UINTHASHINIT); DEBUGASSERT(h->init == CURL_UINTHASHINIT);
if(h->table) { if(h->table) {

View file

@ -1,5 +1,5 @@
#ifndef HEADER_CURL_HASH_OFFT_H #ifndef HEADER_CURL_UINT_HASH_H
#define HEADER_CURL_HASH_OFFT_H #define HEADER_CURL_UINT_HASH_H
/*************************************************************************** /***************************************************************************
* _ _ ____ _ * _ _ ____ _
* Project ___| | | | _ \| | * Project ___| | | | _ \| |
@ -30,32 +30,6 @@
#include "llist.h" #include "llist.h"
struct Curl_hash_offt_entry;
typedef void Curl_hash_offt_dtor(curl_off_t id, void *value);
/* Hash for `curl_off_t` as key */
struct Curl_hash_offt {
struct Curl_hash_offt_entry **table;
Curl_hash_offt_dtor *dtor;
size_t slots;
size_t size;
#ifdef DEBUGBUILD
int init;
#endif
};
void Curl_hash_offt_init(struct Curl_hash_offt *h,
size_t slots,
Curl_hash_offt_dtor *dtor);
void Curl_hash_offt_destroy(struct Curl_hash_offt *h);
bool Curl_hash_offt_set(struct Curl_hash_offt *h, curl_off_t id, void *value);
bool Curl_hash_offt_remove(struct Curl_hash_offt *h, curl_off_t id);
void *Curl_hash_offt_get(struct Curl_hash_offt *h, curl_off_t id);
void Curl_hash_offt_clear(struct Curl_hash_offt *h);
size_t Curl_hash_offt_count(struct Curl_hash_offt *h);
/* A version with unsigned int as key */ /* A version with unsigned int as key */
typedef void Curl_uint_hash_dtor(unsigned int id, void *value); typedef void Curl_uint_hash_dtor(unsigned int id, void *value);
struct uint_hash_entry; struct uint_hash_entry;
@ -76,6 +50,7 @@ void Curl_uint_hash_init(struct uint_hash *h,
unsigned int slots, unsigned int slots,
Curl_uint_hash_dtor *dtor); Curl_uint_hash_dtor *dtor);
void Curl_uint_hash_destroy(struct uint_hash *h); void Curl_uint_hash_destroy(struct uint_hash *h);
void Curl_uint_hash_clear(struct uint_hash *h);
bool Curl_uint_hash_set(struct uint_hash *h, unsigned int id, void *value); bool Curl_uint_hash_set(struct uint_hash *h, unsigned int id, void *value);
bool Curl_uint_hash_remove(struct uint_hash *h, unsigned int id); bool Curl_uint_hash_remove(struct uint_hash *h, unsigned int id);
@ -90,4 +65,4 @@ void Curl_uint_hash_visit(struct uint_hash *h,
Curl_uint_hash_visit_cb *cb, Curl_uint_hash_visit_cb *cb,
void *user_data); void *user_data);
#endif /* HEADER_CURL_HASH_OFFT_H */ #endif /* HEADER_CURL_UINT_HASH_H */

View file

@ -601,6 +601,7 @@ void Curl_conn_free(struct Curl_easy *data, struct connectdata *conn)
#endif #endif
Curl_safefree(conn->destination); Curl_safefree(conn->destination);
Curl_uint_spbset_destroy(&conn->xfers_attached); Curl_uint_spbset_destroy(&conn->xfers_attached);
Curl_hash_destroy(&conn->meta_hash);
free(conn); /* free all the connection oriented data */ free(conn); /* free all the connection oriented data */
} }
@ -3322,6 +3323,15 @@ static void reuse_conn(struct Curl_easy *data,
Curl_conn_free(data, temp); Curl_conn_free(data, temp);
} }
static void conn_meta_freeentry(void *p)
{
(void)p;
/* Will always be FALSE. Cannot use a 0 assert here since compilers
* are not in agreement if they then want a NORETURN attribute or
* not. *sigh* */
DEBUGASSERT(p == NULL);
}
/** /**
* create_conn() sets up a new connectdata struct, or reuses an already * create_conn() sets up a new connectdata struct, or reuses an already
* existing one, and resolves hostname. * existing one, and resolves hostname.
@ -3377,6 +3387,9 @@ static CURLcode create_conn(struct Curl_easy *data,
*in_connect = conn; *in_connect = conn;
/* Do the unfailable inits first, before checks that may early return */ /* Do the unfailable inits first, before checks that may early return */
Curl_hash_init(&conn->meta_hash, 23,
Curl_hash_str, Curl_str_key_compare, conn_meta_freeentry);
/* GSSAPI related inits */ /* GSSAPI related inits */
Curl_sec_conn_init(conn); Curl_sec_conn_init(conn);
@ -3944,3 +3957,23 @@ void Curl_data_priority_clear_state(struct Curl_easy *data)
} }
#endif /* defined(USE_HTTP2) || defined(USE_HTTP3) */ #endif /* defined(USE_HTTP2) || defined(USE_HTTP3) */
CURLcode Curl_conn_meta_set(struct connectdata *conn, const char *key,
void *meta_data, Curl_meta_dtor *meta_dtor)
{
if(!Curl_hash_add2(&conn->meta_hash, CURL_UNCONST(key), strlen(key) + 1,
meta_data, meta_dtor)) {
meta_dtor(CURL_UNCONST(key), strlen(key) + 1, meta_data);
}
return CURLE_OK;
}
void Curl_conn_meta_remove(struct connectdata *conn, const char *key)
{
Curl_hash_delete(&conn->meta_hash, CURL_UNCONST(key), strlen(key) + 1);
}
void *Curl_conn_meta_get(struct connectdata *conn, const char *key)
{
return Curl_hash_pick(&conn->meta_hash, CURL_UNCONST(key), strlen(key) + 1);
}

View file

@ -54,10 +54,18 @@ typedef void Curl_meta_dtor(void *key, size_t key_len, void *meta_data);
* Takes ownership of `meta_data` and destroys it when the call fails. */ * Takes ownership of `meta_data` and destroys it when the call fails. */
CURLcode Curl_meta_set(struct Curl_easy *data, const char *key, CURLcode Curl_meta_set(struct Curl_easy *data, const char *key,
void *meta_data, Curl_meta_dtor *meta_dtor); void *meta_data, Curl_meta_dtor *meta_dtor);
void Curl_meta_clear(struct Curl_easy *data, const char *key); void Curl_meta_remove(struct Curl_easy *data, const char *key);
void *Curl_meta_get(struct Curl_easy *data, const char *key); void *Curl_meta_get(struct Curl_easy *data, const char *key);
void Curl_meta_reset(struct Curl_easy *data); void Curl_meta_reset(struct Curl_easy *data);
/* Set connection meta data for the key. Any existing entry for that
* key will be destroyed.
* Takes ownership of `meta_data` and destroys it when the call fails. */
CURLcode Curl_conn_meta_set(struct connectdata *conn, const char *key,
void *meta_data, Curl_meta_dtor *meta_dtor);
void Curl_conn_meta_remove(struct connectdata *conn, const char *key);
void *Curl_conn_meta_get(struct connectdata *conn, const char *key);
/* Get protocol handler for a URI scheme /* Get protocol handler for a URI scheme
* @param scheme URI scheme, case-insensitive * @param scheme URI scheme, case-insensitive
* @return NULL of handler not found * @return NULL of handler not found

View file

@ -152,7 +152,6 @@ typedef unsigned int curl_prot_t;
#include "http_chunks.h" /* for the structs and enum stuff */ #include "http_chunks.h" /* for the structs and enum stuff */
#include "hostip.h" #include "hostip.h"
#include "hash.h" #include "hash.h"
#include "hash_offt.h"
#include "splay.h" #include "splay.h"
#include "dynbuf.h" #include "dynbuf.h"
#include "dynhds.h" #include "dynhds.h"
@ -750,6 +749,12 @@ struct connectdata {
track the connections in the log output */ track the connections in the log output */
char *destination; /* string carrying normalized hostname+port+scope */ char *destination; /* string carrying normalized hostname+port+scope */
/* `meta_hash` is a general key-value store for implementations
* with the lifetime of the connection.
* Elements need to be added with their own destructor to be invoked when
* the connection is cleaned up (see Curl_hash_add2()).*/
struct Curl_hash meta_hash;
/* 'dns_entry' is the particular host we use. This points to an entry in the /* 'dns_entry' is the particular host we use. This points to an entry in the
DNS cache and it will not get pruned while locked. It gets unlocked in DNS cache and it will not get pruned while locked. It gets unlocked in
multi_done(). This entry will be NULL if the connection is reused as then multi_done(). This entry will be NULL if the connection is reused as then

View file

@ -28,7 +28,7 @@
#include "../urldata.h" #include "../urldata.h"
#include "../hash.h" #include "../hash.h"
#include "../hash_offt.h" #include "../uint-hash.h"
#include "../timeval.h" #include "../timeval.h"
#include "../multiif.h" #include "../multiif.h"
#include "../sendf.h" #include "../sendf.h"

View file

@ -47,7 +47,7 @@
#endif #endif
#include "../urldata.h" #include "../urldata.h"
#include "../hash_offt.h" #include "../uint-hash.h"
#include "../sendf.h" #include "../sendf.h"
#include "../strdup.h" #include "../strdup.h"
#include "../rand.h" #include "../rand.h"

View file

@ -29,7 +29,7 @@
#include <openssl/err.h> #include <openssl/err.h>
#include <openssl/ssl.h> #include <openssl/ssl.h>
#include "../bufq.h" #include "../bufq.h"
#include "../hash_offt.h" #include "../uint-hash.h"
#include "../urldata.h" #include "../urldata.h"
#include "../cfilters.h" #include "../cfilters.h"
#include "../cf-socket.h" #include "../cf-socket.h"

View file

@ -2,7 +2,7 @@
<info> <info>
<keywords> <keywords>
unittest unittest
hash uint_hash
</keywords> </keywords>
</info> </info>
@ -16,7 +16,7 @@ none
unittest unittest
</features> </features>
<name> <name>
Internal hash_offt create/add/destroy testing, exercising clean functions Internal uint_hash create/add/destroy testing, exercising clean functions
</name> </name>
</client> </client>
</testcase> </testcase>

View file

@ -25,13 +25,13 @@
#include "curlx.h" #include "curlx.h"
#include "hash_offt.h" #include "uint-hash.h"
#include "memdebug.h" /* LAST include file */ #include "memdebug.h" /* LAST include file */
static struct Curl_hash_offt hash_static; static struct uint_hash hash_static;
static void mydtor(curl_off_t id, void *elem) static void mydtor(unsigned int id, void *elem)
{ {
int *ptr = (int *)elem; int *ptr = (int *)elem;
(void)id; (void)id;
@ -40,13 +40,13 @@ static void mydtor(curl_off_t id, void *elem)
static CURLcode unit_setup(void) static CURLcode unit_setup(void)
{ {
Curl_hash_offt_init(&hash_static, 15, mydtor); Curl_uint_hash_init(&hash_static, 15, mydtor);
return CURLE_OK; return CURLE_OK;
} }
static void unit_stop(void) static void unit_stop(void)
{ {
Curl_hash_offt_destroy(&hash_static); Curl_uint_hash_destroy(&hash_static);
} }
UNITTEST_START UNITTEST_START
@ -54,34 +54,34 @@ UNITTEST_START
int *value2; int *value2;
bool ok; bool ok;
curl_off_t key = 20; unsigned int key = 20;
curl_off_t key2 = 25; unsigned int key2 = 25;
value = malloc(sizeof(int)); value = malloc(sizeof(int));
abort_unless(value != NULL, "Out of memory"); abort_unless(value != NULL, "Out of memory");
*value = 199; *value = 199;
ok = Curl_hash_offt_set(&hash_static, key, value); ok = Curl_uint_hash_set(&hash_static, key, value);
if(!ok) if(!ok)
free(value); free(value);
abort_unless(ok, "insertion into hash failed"); abort_unless(ok, "insertion into hash failed");
v = Curl_hash_offt_get(&hash_static, key); v = Curl_uint_hash_get(&hash_static, key);
abort_unless(v == value, "lookup present entry failed"); abort_unless(v == value, "lookup present entry failed");
v = Curl_hash_offt_get(&hash_static, key2); v = Curl_uint_hash_get(&hash_static, key2);
abort_unless(!v, "lookup missing entry failed"); abort_unless(!v, "lookup missing entry failed");
Curl_hash_offt_clear(&hash_static); Curl_uint_hash_clear(&hash_static);
/* Attempt to add another key/value pair */ /* Attempt to add another key/value pair */
value2 = malloc(sizeof(int)); value2 = malloc(sizeof(int));
abort_unless(value2 != NULL, "Out of memory"); abort_unless(value2 != NULL, "Out of memory");
*value2 = 204; *value2 = 204;
ok = Curl_hash_offt_set(&hash_static, key2, value2); ok = Curl_uint_hash_set(&hash_static, key2, value2);
if(!ok) if(!ok)
free(value2); free(value2);
abort_unless(ok, "insertion into hash failed"); abort_unless(ok, "insertion into hash failed");
v = Curl_hash_offt_get(&hash_static, key2); v = Curl_uint_hash_get(&hash_static, key2);
abort_unless(v == value2, "lookup present entry failed"); abort_unless(v == value2, "lookup present entry failed");
v = Curl_hash_offt_get(&hash_static, key); v = Curl_uint_hash_get(&hash_static, key);
abort_unless(!v, "lookup missing entry failed"); abort_unless(!v, "lookup missing entry failed");
UNITTEST_STOP UNITTEST_STOP