Home | History | Annotate | Line # | Download | only in internal
      1      1.1  christos /*
      2      1.1  christos  * Copyright 2024-2025 The OpenSSL Project Authors. All Rights Reserved.
      3      1.1  christos  *
      4      1.1  christos  * Licensed under the Apache License 2.0 (the "License").  You may not use
      5      1.1  christos  * this file except in compliance with the License.  You can obtain a copy
      6      1.1  christos  * in the file LICENSE in the source distribution or at
      7      1.1  christos  * https://www.openssl.org/source/license.html
      8      1.1  christos  */
      9      1.1  christos 
     10      1.1  christos #ifndef OPENSSL_HASHTABLE_H
     11  1.1.1.2  christos #define OPENSSL_HASHTABLE_H
     12  1.1.1.2  christos #pragma once
     13      1.1  christos 
     14      1.1  christos #include <stddef.h>
     15      1.1  christos #include <stdint.h>
     16      1.1  christos #include <openssl/e_os2.h>
     17      1.1  christos #include <internal/rcu.h>
     18      1.1  christos #include "crypto/context.h"
     19      1.1  christos 
     20      1.1  christos typedef struct ht_internal_st HT;
     21      1.1  christos 
     22      1.1  christos /*
     23      1.1  christos  * Represents a key to a hashtable
     24      1.1  christos  */
     25      1.1  christos typedef struct ht_key_header_st {
     26      1.1  christos     size_t keysize;
     27      1.1  christos     uint8_t *keybuf;
     28      1.1  christos } HT_KEY;
     29      1.1  christos 
     30      1.1  christos /*
     31      1.1  christos  * Represents a value in the hash table
     32      1.1  christos  */
     33      1.1  christos typedef struct ht_value_st {
     34      1.1  christos     void *value;
     35      1.1  christos     uintptr_t *type_id;
     36      1.1  christos     HT_KEY key;
     37      1.1  christos } HT_VALUE;
     38      1.1  christos 
     39      1.1  christos /*
     40      1.1  christos  * Represents a list of values filtered from a hash table
     41      1.1  christos  */
     42      1.1  christos typedef struct ht_value_list_st {
     43      1.1  christos     size_t list_len;
     44      1.1  christos     HT_VALUE **list;
     45      1.1  christos } HT_VALUE_LIST;
     46      1.1  christos 
     47      1.1  christos /*
     48      1.1  christos  * Hashtable configuration
     49      1.1  christos  */
     50      1.1  christos typedef struct ht_config_st {
     51      1.1  christos     OSSL_LIB_CTX *ctx;
     52      1.1  christos     void (*ht_free_fn)(HT_VALUE *obj);
     53      1.1  christos     uint64_t (*ht_hash_fn)(uint8_t *key, size_t keylen);
     54      1.1  christos     size_t init_neighborhoods;
     55      1.1  christos     uint32_t collision_check;
     56      1.1  christos     uint32_t lockless_reads;
     57      1.1  christos } HT_CONFIG;
     58      1.1  christos 
     59      1.1  christos /*
     60      1.1  christos  * Hashtable key rules
     61      1.1  christos  * Any struct can be used to formulate a hash table key, as long as the
     62      1.1  christos  * following rules
     63      1.1  christos  * 1) The first element of the struct defining the key must be an HT_KEY
     64      1.1  christos  * 2) All struct elements must have a compile time defined length
     65      1.1  christos  * 3) Pointers can be used, but the value of the pointer, rather than
     66      1.1  christos  *    the contents of the address it points to will be used to compute
     67      1.1  christos  *    the hash
     68      1.1  christos  * The key definition macros will assist with enforcing these rules
     69      1.1  christos  */
     70      1.1  christos 
     71      1.1  christos /*
     72      1.1  christos  * Starts the definition of a hash table key
     73      1.1  christos  */
     74      1.1  christos #define HT_START_KEY_DEFN(keyname) \
     75  1.1.1.2  christos     typedef struct keyname##_st {  \
     76  1.1.1.2  christos         HT_KEY key_header;         \
     77  1.1.1.2  christos         struct {
     78      1.1  christos 
     79      1.1  christos /*
     80      1.1  christos  * Ends a hash table key definitions
     81      1.1  christos  */
     82      1.1  christos #define HT_END_KEY_DEFN(keyname) \
     83  1.1.1.2  christos     }                            \
     84  1.1.1.2  christos     keyfields;                   \
     85  1.1.1.2  christos     }                            \
     86  1.1.1.2  christos     keyname;
     87      1.1  christos 
     88      1.1  christos /*
     89      1.1  christos  * Defines a field in a hash table key
     90      1.1  christos  */
     91      1.1  christos #define HT_DEF_KEY_FIELD(name, type) type name;
     92      1.1  christos 
     93      1.1  christos /*
     94      1.1  christos  * convenience macro to define a static char
     95      1.1  christos  * array field in a hash table key
     96      1.1  christos  */
     97      1.1  christos #define HT_DEF_KEY_FIELD_CHAR_ARRAY(name, size) \
     98  1.1.1.2  christos     HT_DEF_KEY_FIELD(name[size], char)
     99      1.1  christos 
    100      1.1  christos /*
    101      1.1  christos  * Defines a uint8_t (blob) field in a hash table key
    102      1.1  christos  */
    103      1.1  christos #define HT_DEF_KEY_FIELD_UINT8T_ARRAY(name, size) \
    104      1.1  christos     HT_DEF_KEY_FIELD(name[size], uint8_t)
    105      1.1  christos 
    106      1.1  christos /*
    107      1.1  christos  * Initializes a key
    108      1.1  christos  */
    109  1.1.1.2  christos #define HT_INIT_KEY(key)                                                \
    110  1.1.1.2  christos     do {                                                                \
    111  1.1.1.2  christos         memset((key), 0, sizeof(*(key)));                               \
    112  1.1.1.2  christos         (key)->key_header.keysize = (sizeof(*(key)) - sizeof(HT_KEY));  \
    113  1.1.1.2  christos         (key)->key_header.keybuf = (((uint8_t *)key) + sizeof(HT_KEY)); \
    114  1.1.1.2  christos     } while (0)
    115      1.1  christos 
    116      1.1  christos /*
    117      1.1  christos  * Resets a hash table key to a known state
    118      1.1  christos  */
    119      1.1  christos #define HT_KEY_RESET(key) memset((key)->key_header.keybuf, 0, (key)->key_header.keysize)
    120      1.1  christos 
    121      1.1  christos /*
    122      1.1  christos  * Sets a scalar field in a hash table key
    123      1.1  christos  */
    124      1.1  christos #define HT_SET_KEY_FIELD(key, member, value) (key)->keyfields.member = value;
    125      1.1  christos 
    126      1.1  christos /*
    127      1.1  christos  * Sets a string field in a hash table key, preserving
    128      1.1  christos  * null terminator
    129      1.1  christos  */
    130  1.1.1.2  christos #define HT_SET_KEY_STRING(key, member, value)                                             \
    131  1.1.1.2  christos     do {                                                                                  \
    132  1.1.1.2  christos         if ((value) != NULL)                                                              \
    133  1.1.1.2  christos             strncpy((key)->keyfields.member, value, sizeof((key)->keyfields.member) - 1); \
    134  1.1.1.2  christos     } while (0)
    135      1.1  christos 
    136      1.1  christos /*
    137      1.1  christos  * This is the same as HT_SET_KEY_STRING, except that it uses
    138      1.1  christos  * ossl_ht_strcase to make the value being passed case insensitive
    139      1.1  christos  * This is useful for instances in which we want upper and lower case
    140      1.1  christos  * key value to hash to the same entry
    141      1.1  christos  */
    142  1.1.1.2  christos #define HT_SET_KEY_STRING_CASE(key, member, value)                                            \
    143  1.1.1.2  christos     do {                                                                                      \
    144  1.1.1.2  christos         ossl_ht_strcase((key)->keyfields.member, value, sizeof((key)->keyfields.member) - 1); \
    145  1.1.1.2  christos     } while (0)
    146      1.1  christos 
    147      1.1  christos /*
    148      1.1  christos  * Same as HT_SET_KEY_STRING but also takes length of the string.
    149      1.1  christos  */
    150  1.1.1.2  christos #define HT_SET_KEY_STRING_N(key, member, value, len)                                          \
    151  1.1.1.2  christos     do {                                                                                      \
    152  1.1.1.2  christos         if ((value) != NULL) {                                                                \
    153  1.1.1.2  christos             if (len < sizeof((key)->keyfields.member))                                        \
    154  1.1.1.2  christos                 strncpy((key)->keyfields.member, value, len);                                 \
    155  1.1.1.2  christos             else                                                                              \
    156  1.1.1.2  christos                 strncpy((key)->keyfields.member, value, sizeof((key)->keyfields.member) - 1); \
    157  1.1.1.2  christos         }                                                                                     \
    158  1.1.1.2  christos     } while (0)
    159      1.1  christos 
    160      1.1  christos /* Same as HT_SET_KEY_STRING_CASE but also takes length of the string. */
    161  1.1.1.2  christos #define HT_SET_KEY_STRING_CASE_N(key, member, value, len)                                         \
    162  1.1.1.2  christos     do {                                                                                          \
    163  1.1.1.2  christos         if (len < sizeof((key)->keyfields.member))                                                \
    164  1.1.1.2  christos             ossl_ht_strcase((key)->keyfields.member, value, len);                                 \
    165  1.1.1.2  christos         else                                                                                      \
    166  1.1.1.2  christos             ossl_ht_strcase((key)->keyfields.member, value, sizeof((key)->keyfields.member) - 1); \
    167  1.1.1.2  christos     } while (0)
    168      1.1  christos 
    169      1.1  christos /*
    170      1.1  christos  * Sets a uint8_t (blob) field in a hash table key
    171      1.1  christos  */
    172  1.1.1.2  christos #define HT_SET_KEY_BLOB(key, member, value, len)         \
    173  1.1.1.2  christos     do {                                                 \
    174  1.1.1.2  christos         if (value != NULL)                               \
    175  1.1.1.2  christos             memcpy((key)->keyfields.member, value, len); \
    176  1.1.1.2  christos     } while (0)
    177      1.1  christos 
    178      1.1  christos /*
    179      1.1  christos  * Converts a defined key type to an HT_KEY
    180      1.1  christos  */
    181      1.1  christos #define TO_HT_KEY(key) &(key)->key_header
    182      1.1  christos 
    183      1.1  christos /*
    184      1.1  christos  * Converts an HT_KEY back to its defined
    185      1.1  christos  * type
    186      1.1  christos  */
    187      1.1  christos #define FROM_HT_KEY(key, type) (type)(key)
    188      1.1  christos 
    189      1.1  christos /*
    190      1.1  christos  * Implements the following type safe operations for a hash table
    191      1.1  christos  * ossl_ht_NAME_TYPE_insert - insert a value to a hash table of type TYPE
    192      1.1  christos  * ossl_ht_NAME_TYPE_get - gets a value of a specific type from the hash table
    193      1.1  christos  * ossl_ht_NAME_TYPE_from_value - converts an HT_VALUE to its type
    194      1.1  christos  * ossl_ht_NAME_TYPE_to_value - converts a TYPE to an HT_VALUE
    195      1.1  christos  * ossl_ht_NAME_TYPE_type - boolean to detect if a value is of TYPE
    196      1.1  christos  */
    197  1.1.1.2  christos #define IMPLEMENT_HT_VALUE_TYPE_FNS(vtype, name, pfx)                          \
    198  1.1.1.2  christos     static uintptr_t name##_##vtype##_id = 0;                                  \
    199  1.1.1.2  christos     pfx ossl_unused int ossl_ht_##name##_##vtype##_insert(HT *h, HT_KEY *key,  \
    200  1.1.1.2  christos         vtype *data,                                                           \
    201  1.1.1.2  christos         vtype **olddata)                                                       \
    202  1.1.1.2  christos     {                                                                          \
    203  1.1.1.2  christos         HT_VALUE inval;                                                        \
    204  1.1.1.2  christos         HT_VALUE *oval = NULL;                                                 \
    205  1.1.1.2  christos         int rc;                                                                \
    206      1.1  christos                                                                                \
    207  1.1.1.2  christos         inval.value = data;                                                    \
    208  1.1.1.2  christos         inval.type_id = &name##_##vtype##_id;                                  \
    209  1.1.1.2  christos         rc = ossl_ht_insert(h, key, &inval, olddata == NULL ? NULL : &oval);   \
    210  1.1.1.2  christos         if (oval != NULL)                                                      \
    211  1.1.1.2  christos             *olddata = (vtype *)oval->value;                                   \
    212  1.1.1.2  christos         return rc;                                                             \
    213  1.1.1.2  christos     }                                                                          \
    214      1.1  christos                                                                                \
    215  1.1.1.2  christos     pfx ossl_unused vtype *ossl_ht_##name##_##vtype##_from_value(HT_VALUE *v)  \
    216  1.1.1.2  christos     {                                                                          \
    217  1.1.1.2  christos         uintptr_t *expect_type = &name##_##vtype##_id;                         \
    218  1.1.1.2  christos         if (v == NULL)                                                         \
    219  1.1.1.2  christos             return NULL;                                                       \
    220  1.1.1.2  christos         if (v->type_id != expect_type)                                         \
    221  1.1.1.2  christos             return NULL;                                                       \
    222  1.1.1.2  christos         return (vtype *)v->value;                                              \
    223  1.1.1.2  christos     }                                                                          \
    224      1.1  christos                                                                                \
    225  1.1.1.2  christos     pfx ossl_unused vtype *ossl_unused ossl_ht_##name##_##vtype##_get(HT *h,   \
    226  1.1.1.2  christos         HT_KEY *key,                                                           \
    227  1.1.1.2  christos         HT_VALUE **v)                                                          \
    228  1.1.1.2  christos     {                                                                          \
    229  1.1.1.2  christos         HT_VALUE *vv;                                                          \
    230  1.1.1.2  christos         vv = ossl_ht_get(h, key);                                              \
    231  1.1.1.2  christos         if (vv == NULL)                                                        \
    232  1.1.1.2  christos             return NULL;                                                       \
    233  1.1.1.2  christos         *v = ossl_rcu_deref(&vv);                                              \
    234  1.1.1.2  christos         return ossl_ht_##name##_##vtype##_from_value(*v);                      \
    235  1.1.1.2  christos     }                                                                          \
    236      1.1  christos                                                                                \
    237  1.1.1.2  christos     pfx ossl_unused HT_VALUE *ossl_ht_##name##_##vtype##_to_value(vtype *data, \
    238  1.1.1.2  christos         HT_VALUE *v)                                                           \
    239  1.1.1.2  christos     {                                                                          \
    240  1.1.1.2  christos         v->type_id = &name##_##vtype##_id;                                     \
    241  1.1.1.2  christos         v->value = data;                                                       \
    242  1.1.1.2  christos         return v;                                                              \
    243  1.1.1.2  christos     }                                                                          \
    244      1.1  christos                                                                                \
    245  1.1.1.2  christos     pfx ossl_unused int ossl_ht_##name##_##vtype##_type(HT_VALUE *h)           \
    246  1.1.1.2  christos     {                                                                          \
    247  1.1.1.2  christos         return h->type_id == &name##_##vtype##_id;                             \
    248  1.1.1.2  christos     }
    249  1.1.1.2  christos 
    250  1.1.1.2  christos #define DECLARE_HT_VALUE_TYPE_FNS(vtype, name)                               \
    251  1.1.1.2  christos     int ossl_ht_##name##_##vtype##_insert(HT *h, HT_KEY *key, vtype *data,   \
    252  1.1.1.2  christos         vtype **olddata);                                                    \
    253  1.1.1.2  christos     vtype *ossl_ht_##name##_##vtype##_from_value(HT_VALUE *v);               \
    254  1.1.1.2  christos     vtype *ossl_unused ossl_ht_##name##_##vtype##_get(HT *h,                 \
    255  1.1.1.2  christos         HT_KEY *key,                                                         \
    256  1.1.1.2  christos         HT_VALUE **v);                                                       \
    257  1.1.1.2  christos     HT_VALUE *ossl_ht_##name##_##vtype##_to_value(vtype *data, HT_VALUE *v); \
    258  1.1.1.2  christos     int ossl_ht_##name##_##vtype##_type(HT_VALUE *h);
    259      1.1  christos 
    260      1.1  christos /*
    261      1.1  christos  * Helper function to construct case insensitive keys
    262      1.1  christos  */
    263      1.1  christos static void ossl_unused ossl_ht_strcase(char *tgt, const char *src, int len)
    264      1.1  christos {
    265      1.1  christos     int i;
    266      1.1  christos #if defined(CHARSET_EBCDIC) && !defined(CHARSET_EBCDIC_TEST)
    267      1.1  christos     const long int case_adjust = ~0x40;
    268      1.1  christos #else
    269      1.1  christos     const long int case_adjust = ~0x20;
    270      1.1  christos #endif
    271      1.1  christos 
    272      1.1  christos     if (src == NULL)
    273      1.1  christos         return;
    274      1.1  christos 
    275      1.1  christos     for (i = 0; src[i] != '\0' && i < len; i++)
    276      1.1  christos         tgt[i] = case_adjust & src[i];
    277      1.1  christos }
    278      1.1  christos 
    279      1.1  christos /*
    280      1.1  christos  * Create a new hashtable
    281      1.1  christos  */
    282      1.1  christos HT *ossl_ht_new(const HT_CONFIG *conf);
    283      1.1  christos 
    284      1.1  christos /*
    285      1.1  christos  * Frees a hash table, potentially freeing all elements
    286      1.1  christos  */
    287      1.1  christos void ossl_ht_free(HT *htable);
    288      1.1  christos 
    289      1.1  christos /*
    290      1.1  christos  * Lock the table for reading
    291      1.1  christos  */
    292      1.1  christos void ossl_ht_read_lock(HT *htable);
    293      1.1  christos 
    294      1.1  christos /*
    295      1.1  christos  * Lock the table for writing
    296      1.1  christos  */
    297      1.1  christos void ossl_ht_write_lock(HT *htable);
    298      1.1  christos 
    299      1.1  christos /*
    300      1.1  christos  * Read unlock
    301      1.1  christos  */
    302      1.1  christos void ossl_ht_read_unlock(HT *htable);
    303      1.1  christos 
    304      1.1  christos /*
    305      1.1  christos  * Write unlock
    306      1.1  christos  */
    307  1.1.1.2  christos void ossl_ht_write_unlock(HT *htable);
    308      1.1  christos 
    309      1.1  christos /*
    310      1.1  christos  * Empties a hash table, potentially freeing all elements
    311      1.1  christos  */
    312  1.1.1.2  christos int ossl_ht_flush(HT *htable);
    313      1.1  christos 
    314      1.1  christos /*
    315      1.1  christos  * Inserts an element to a hash table, optionally returning
    316      1.1  christos  * replaced data to caller
    317      1.1  christos  * Returns 1 if the insert was successful, 0 on error
    318      1.1  christos  */
    319      1.1  christos int ossl_ht_insert(HT *htable, HT_KEY *key, HT_VALUE *data,
    320  1.1.1.2  christos     HT_VALUE **olddata);
    321      1.1  christos 
    322      1.1  christos /*
    323      1.1  christos  * Deletes a value from a hash table, based on key
    324      1.1  christos  * Returns 1 if the key was removed, 0 if they key was not found
    325      1.1  christos  */
    326      1.1  christos int ossl_ht_delete(HT *htable, HT_KEY *key);
    327      1.1  christos 
    328      1.1  christos /*
    329      1.1  christos  * Returns number of elements in the hash table
    330      1.1  christos  */
    331      1.1  christos size_t ossl_ht_count(HT *htable);
    332      1.1  christos 
    333      1.1  christos /*
    334      1.1  christos  * Iterates over each element in the table.
    335      1.1  christos  * aborts the loop when cb returns 0
    336      1.1  christos  * Contents of elements in the list may be modified during
    337      1.1  christos  * this traversal, assuming proper thread safety is observed while doing
    338      1.1  christos  * so (holding the table write lock is sufficient).  However, elements of the
    339      1.1  christos  * table may not be inserted or removed while iterating.
    340      1.1  christos  */
    341      1.1  christos void ossl_ht_foreach_until(HT *htable, int (*cb)(HT_VALUE *obj, void *arg),
    342  1.1.1.2  christos     void *arg);
    343      1.1  christos /*
    344      1.1  christos  * Returns a list of elements in a hash table based on
    345      1.1  christos  * filter function return value.  Returns NULL on error,
    346      1.1  christos  * or an HT_VALUE_LIST object on success.  Note it is possible
    347      1.1  christos  * That a list will be returned with 0 entries, if none were found.
    348  1.1.1.2  christos  * The zero length list must still be freed via ossl_ht_value_list_free
    349      1.1  christos  */
    350      1.1  christos HT_VALUE_LIST *ossl_ht_filter(HT *htable, size_t max_len,
    351  1.1.1.2  christos     int (*filter)(HT_VALUE *obj, void *arg),
    352  1.1.1.2  christos     void *arg);
    353      1.1  christos /*
    354      1.1  christos  * Frees the list returned from ossl_ht_filter
    355      1.1  christos  */
    356      1.1  christos void ossl_ht_value_list_free(HT_VALUE_LIST *list);
    357      1.1  christos 
    358      1.1  christos /*
    359      1.1  christos  * Fetches a value from the hash table, based
    360      1.1  christos  * on key.  Returns NULL if the element was not found.
    361      1.1  christos  */
    362      1.1  christos HT_VALUE *ossl_ht_get(HT *htable, HT_KEY *key);
    363      1.1  christos 
    364      1.1  christos #endif
    365