LCOV - code coverage report
Current view: top level - src - napr_hash.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 88.3 % 128 113
Test Date: 2025-10-15 21:43:52 Functions: 100.0 % 14 14

            Line data    Source code
       1              : /**
       2              :  * @file napr_hash.c
       3              :  * @brief Implementation of the high-performance hash table.
       4              :  * @ingroup DataStructures
       5              :  */
       6              : /*
       7              :  * Copyright (C) 2007 François Pesce : francois.pesce (at) gmail (dot) com
       8              :  *
       9              :  * Licensed under the Apache License, Version 2.0 (the "License");
      10              :  * you may not use this file except in compliance with the License.
      11              :  * You may obtain a copy of the License at
      12              :  *
      13              :  *      http://www.apache.org/licenses/LICENSE-2.0
      14              :  *
      15              :  * Unless required by applicable law or agreed to in writing, software
      16              :  * distributed under the License is distributed on an "AS IS" BASIS,
      17              :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      18              :  * See the License for the specific language governing permissions and
      19              :  * limitations under the License.
      20              :  */
      21              : 
      22              : #include "debug.h"
      23              : #include "napr_hash.h"
      24              : 
      25              : #define XXH_STATIC_LINKING_ONLY
      26              : #include "xxhash.h"
      27              : 
      28              : #define hashsize(n) ((apr_size_t)1<<(n))
      29              : #define hashmask(n) (hashsize(n)-1)
      30              : 
      31          226 : static const void *str_get_key(const void *opaque)
      32              : {
      33          226 :     return opaque;
      34              : }
      35              : 
      36          235 : static apr_size_t str_get_key_len(const void *opaque)
      37              : {
      38          235 :     const char *str = opaque;
      39          235 :     return strlen(str);
      40              : }
      41              : 
      42          126 : static int str_key_cmp(const void *opaque1, const void *opaque2, apr_size_t len)
      43              : {
      44          126 :     const char *str1 = opaque1;
      45          126 :     const char *str2 = opaque2;
      46              : 
      47          126 :     return memcmp(str1, str2, len);
      48              : }
      49              : 
      50          388 : static apr_uint32_t str_hash(register const void *opaque, register apr_size_t len)
      51              : {
      52              :     /* Use XXH32 for string hashing - faster and more consistent with file hashing */
      53          388 :     return XXH32(opaque, len, 0x1337cafe);
      54              : }
      55              : 
      56              : struct napr_hash_index_t
      57              : {
      58              :     napr_hash_t *hash;
      59              :     apr_size_t bucket;
      60              :     apr_size_t element;         /* of a bucket */
      61              : };
      62              : 
      63              : struct napr_hash_t
      64              : {
      65              :     /* void *table[size][ffactor] */
      66              :     void ***table;
      67              :     /* apr_size_t table[size] contain the number of element for each bucket */
      68              :     apr_size_t *filling_table;
      69              :     /* parent pool */
      70              :     apr_pool_t *pool;
      71              :     /* own pool that will be cleaned if a grow of the table occured */
      72              :     apr_pool_t *own_pool;
      73              :     /* Function to get the key from the datum */
      74              :     get_key_callback_fn_t *get_key;
      75              :     /* Function to get the key len from the datum */
      76              :     get_key_len_callback_fn_t *get_key_len;
      77              :     /* Function to compare two keys */
      78              :     key_cmp_callback_fn_t *key_cmp;
      79              :     /* hash function */
      80              :     hash_callback_fn_t *hash;
      81              : 
      82              :     /* the number of element contained in all the buckets of the table */
      83              :     apr_size_t nel;
      84              :     /* the number of buckets */
      85              :     apr_size_t size;
      86              :     /* desired density */
      87              :     apr_size_t ffactor;
      88              :     /* the binary mask to apply to the result of a hash function that return a
      89              :      * number < size */
      90              :     apr_uint32_t mask;
      91              :     /* size of the hash is hashsize(power) as mask is hashmask(power) */
      92              :     unsigned char power;
      93              : };
      94              : 
      95           23 : extern napr_hash_t *napr_hash_str_make(apr_pool_t *pool, apr_size_t nel, apr_size_t ffactor)
      96              : {
      97           23 :     return napr_hash_make(pool, nel, ffactor, str_get_key, str_get_key_len, str_key_cmp, str_hash);
      98              : }
      99              : 
     100           67 : extern napr_hash_t *napr_hash_make(apr_pool_t *pool, apr_size_t nel, apr_size_t ffactor, get_key_callback_fn_t get_key,
     101              :                                    get_key_len_callback_fn_t get_key_len, key_cmp_callback_fn_t key_cmp,
     102              :                                    hash_callback_fn_t hash)
     103              : {
     104              :     napr_hash_t *result;
     105              :     apr_status_t status;
     106              : 
     107           67 :     if (NULL == (result = apr_pcalloc(pool, sizeof(struct napr_hash_t)))) {
     108            0 :         DEBUG_ERR("allocation error");
     109            0 :         return NULL;
     110              :     }
     111              : 
     112          633 :     while (hashsize(result->power) < nel)
     113          566 :         result->power++;
     114              : 
     115           67 :     result->size = hashsize(result->power);
     116           67 :     result->mask = hashmask(result->power);
     117           67 :     result->ffactor = ffactor;
     118           67 :     result->get_key = get_key;
     119           67 :     result->get_key_len = get_key_len;
     120           67 :     result->key_cmp = key_cmp;
     121           67 :     result->hash = hash;
     122           67 :     result->pool = pool;
     123              : 
     124           67 :     if (APR_SUCCESS != (status = apr_pool_create(&(result->own_pool), pool))) {
     125              :         char errbuf[128];
     126            0 :         DEBUG_ERR("error calling apr_pool_create: %s", apr_strerror(status, errbuf, 128));
     127            0 :         return NULL;
     128              :     }
     129              :     /*DEBUG_DBG("readjusting size to %" APR_SIZE_T_FMT " to store %" APR_SIZE_T_FMT " elements", result->size, nel); */
     130              :     /*DEBUG_DBG("bit mask will be 0x%x", result->mask); */
     131              : 
     132           67 :     if (NULL == (result->table = apr_pcalloc(result->own_pool, result->size * sizeof(void **)))) {
     133            0 :         DEBUG_ERR("allocation error");
     134            0 :         return NULL;
     135              :     }
     136              : 
     137           67 :     if (NULL == (result->filling_table = apr_pcalloc(result->own_pool, result->size * sizeof(apr_size_t)))) {
     138            0 :         DEBUG_ERR("allocation error");
     139            0 :         return NULL;
     140              :     }
     141              : 
     142           67 :     return result;
     143              : }
     144              : 
     145          656 : extern void *napr_hash_search(napr_hash_t *hash, const void *key, apr_size_t key_len, apr_uint32_t *hash_value)
     146              : {
     147              :     apr_uint32_t key_hash;
     148              :     apr_size_t i, nel, bucket;
     149              : 
     150          656 :     key_hash = hash->hash(key, key_len);
     151              : 
     152          656 :     if (NULL != hash_value)
     153          459 :         *hash_value = key_hash;
     154              : 
     155          656 :     bucket = key_hash & hash->mask;
     156          656 :     if (0 != (nel = hash->filling_table[bucket])) {
     157          387 :         for (i = 0; i < nel; i++) {
     158              :             /*DEBUG_DBG( "key[%p] bucket[%"APR_SIZE_T_FMT"][%"APR_SIZE_T_FMT"]=[%p]", key, bucket, i, hash->get_key(hash->table[bucket][i])); */
     159          364 :             if (key_len == hash->get_key_len(hash->table[bucket][i]))
     160          355 :                 if (0 == (hash->key_cmp(key, hash->get_key(hash->table[bucket][i]), key_len)))
     161          320 :                     return hash->table[bucket][i];
     162              :         }
     163              :     }
     164              : 
     165          336 :     return NULL;
     166              : }
     167              : 
     168           10 : static inline apr_status_t napr_hash_rebuild(napr_hash_t *hash)
     169              : {
     170              :     napr_hash_t *tmp;
     171              :     apr_size_t i, j;
     172              :     apr_status_t status;
     173              : 
     174           10 :     if (NULL ==
     175              :         (tmp =
     176           10 :          napr_hash_make(hash->pool, hashsize(hash->power + 1), hash->ffactor, hash->get_key, hash->get_key_len,
     177              :                         hash->key_cmp, hash->hash))) {
     178            0 :         DEBUG_ERR("error calling napr_hash_init");
     179            0 :         return APR_EGENERAL;
     180              :     }
     181              : 
     182         1034 :     for (i = 0; i < hash->size; i++) {
     183         1114 :         for (j = 0; j < hash->filling_table[i]; j++) {
     184              :             /*
     185              :              * no need to do doublon test here as we take data directly from a
     186              :              * hash table
     187              :              */
     188           90 :             if (APR_SUCCESS !=
     189              :                 (status =
     190           90 :                  napr_hash_set(tmp, hash->table[i][j],
     191           90 :                                hash->hash(hash->get_key(hash->table[i][j]), hash->get_key_len(hash->table[i][j]))))) {
     192              :                 char errbuf[128];
     193            0 :                 DEBUG_ERR("error calling napr_hash_set: %s", apr_strerror(status, errbuf, 128));
     194            0 :                 return status;
     195              :             }
     196              :         }
     197              :     }
     198           10 :     hash->table = tmp->table;
     199           10 :     hash->filling_table = tmp->filling_table;
     200           10 :     hash->nel = tmp->nel;
     201           10 :     hash->size = tmp->size;
     202           10 :     hash->mask = tmp->mask;
     203           10 :     hash->power = tmp->power;
     204           10 :     apr_pool_destroy(hash->own_pool);
     205           10 :     hash->own_pool = tmp->own_pool;
     206              : 
     207           10 :     return APR_SUCCESS;
     208              : }
     209              : 
     210            6 : extern void napr_hash_remove(napr_hash_t *hash, void *data, apr_uint32_t hash_value)
     211              : {
     212              :     apr_size_t nel, bucket, i, key_len;
     213              :     const void *key;
     214              : 
     215            6 :     bucket = hash_value & hash->mask;
     216            6 :     key = hash->get_key(data);
     217            6 :     key_len = hash->get_key_len(data);
     218            6 :     if (0 != (nel = hash->filling_table[bucket])) {
     219            8 :         for (i = 0; i < nel; i++) {
     220              :             //DEBUG_DBG( "key[%.*s] bucket[%i]=[%.*s]", key_len, key, i, hash->get_key_len(hash->table[bucket][i]), hash->get_key(hash->table[bucket][i]));
     221            8 :             if (key_len == hash->get_key_len(hash->table[bucket][i]))
     222            8 :                 if (0 == (hash->key_cmp(key, hash->get_key(hash->table[bucket][i]), key_len))) {
     223              :                     /* Remove it, by replacing with the last element if present */
     224            6 :                     if (i != nel - 1) {
     225            2 :                         hash->table[bucket][i] = hash->table[bucket][nel - 1];
     226            2 :                         hash->table[bucket][nel - 1] = NULL;
     227              :                     }
     228              :                     else {
     229            4 :                         hash->table[bucket][i] = NULL;
     230              :                     }
     231            6 :                     hash->filling_table[bucket]--;
     232            6 :                     hash->nel--;
     233            6 :                     break;
     234              :                 }
     235              :         }
     236              :     }
     237              :     else {
     238            0 :         DEBUG_DBG("try to remove something that is not here");
     239              :     }
     240            6 : }
     241              : 
     242          316 : extern apr_status_t napr_hash_set(napr_hash_t *hash, void *data, apr_uint32_t hash_value)
     243              : {
     244              :     apr_size_t nel, bucket;
     245              :     apr_status_t status;
     246              : 
     247          316 :     bucket = hash_value & hash->mask;
     248              : 
     249          316 :     if ((0 == (nel = hash->filling_table[bucket])) && (NULL == hash->table[bucket])) {
     250          293 :         hash->table[bucket] = (void **) apr_pcalloc(hash->own_pool, hash->ffactor * sizeof(void *));
     251              :     }
     252              :     // DEBUG_DBG( "set data %.*s in bucket %u at nel %u", hash->datum_get_key_len(data), hash->datum_get_key(data), bucket, nel);
     253          316 :     hash->table[bucket][nel] = data;
     254          316 :     hash->filling_table[bucket]++;
     255          316 :     hash->nel++;
     256              : 
     257          316 :     if (hash->ffactor <= hash->filling_table[bucket]) {
     258              :         // int i;
     259              :         // DEBUG_DBG( "rebuilding hash table, because there's %u element in bucket %u ffactor is %u (total element = %u)", 
     260              :         //      hash->filling_table[bucket], bucket, hash->ffactor, hash->nel);
     261              :         // for(i = 0; i < hash->ffactor; i++) {
     262              :         //     DEBUG_DBG( "%.*s", hash->datum_get_key_len(hash->table[bucket][i]), hash->datum_get_key(hash->table[bucket][i]));
     263              :         // }
     264              : 
     265           10 :         if (APR_SUCCESS != (status = napr_hash_rebuild(hash))) {
     266              :             char errbuf[128];
     267            0 :             DEBUG_ERR("error calling napr_hash_rebuild: %s", apr_strerror(status, errbuf, 128));
     268            0 :             return status;
     269              :         }
     270              :     }
     271              : 
     272          316 :     return APR_SUCCESS;
     273              : }
     274              : 
     275            1 : apr_pool_t *napr_hash_pool_get(const napr_hash_t *thehash)
     276              : {
     277            1 :     return thehash->pool;
     278              : }
     279              : 
     280           24 : napr_hash_index_t *napr_hash_first(apr_pool_t *pool, napr_hash_t *hash)
     281              : {
     282              :     napr_hash_index_t *hash_index;
     283           24 :     hash_index = apr_palloc(pool, sizeof(struct napr_hash_index_t));
     284           24 :     hash_index->hash = hash;
     285           24 :     hash_index->bucket = 0;
     286           24 :     hash_index->element = 0;
     287              : 
     288           24 :     if (hash->filling_table[0] > 0)
     289            1 :         return hash_index;
     290              : 
     291           23 :     return napr_hash_next(hash_index);
     292              : }
     293              : 
     294           93 : napr_hash_index_t *napr_hash_next(napr_hash_index_t *index)
     295              : {
     296           93 :     if ((0 != index->hash->filling_table[index->bucket])
     297           70 :         && (index->element < (index->hash->filling_table[index->bucket] - 1))) {
     298            4 :         index->element++;
     299            4 :         return index;
     300              :     }
     301              :     else {
     302           89 :         index->element = 0;
     303        90244 :         for (index->bucket += 1; index->bucket < index->hash->size; index->bucket++) {
     304        90220 :             if (0 != index->hash->filling_table[index->bucket])
     305           65 :                 break;
     306              :         }
     307           89 :         if (index->bucket < index->hash->size)
     308           65 :             return index;
     309              :     }
     310              : 
     311           24 :     return NULL;
     312              : }
     313              : 
     314           70 : void napr_hash_this(napr_hash_index_t *hi, const void **key, apr_size_t *klen, void **val)
     315              : {
     316           70 :     if (key)
     317            8 :         *key = hi->hash->get_key(hi->hash->table[hi->bucket][hi->element]);
     318           70 :     if (klen)
     319            8 :         *klen = hi->hash->get_key_len(hi->hash->table[hi->bucket][hi->element]);
     320           70 :     if (val)
     321           70 :         *val = hi->hash->table[hi->bucket][hi->element];
     322           70 : }
        

Generated by: LCOV version 2.0-1