LCOV - code coverage report
Current view: top level - src - hta.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 109 124 87.9 %
Date: 2016-12-21 02:12:01 Functions: 17 18 94.4 %

          Line data    Source code
       1             : /***************************************************************************//**
       2             : 
       3             :   @file         hta.c
       4             : 
       5             :   @author       Stephen Brennan
       6             : 
       7             :   @date         Created Tuesday, 20 September 2016
       8             : 
       9             :   @brief        Hash Table for Any data
      10             : 
      11             :   @copyright    Copyright (c) 2016, Stephen Brennan.  Released under the
      12             :                 Revised BSD License.  See the LICENSE.txt file for details.
      13             : 
      14             :   This is based on my original hashtable.c implementation, but aims to address
      15             :   the observation that using DATA is inflexible compared to simply storing
      16             :   blocks of memory.
      17             : 
      18             : *******************************************************************************/
      19             : 
      20             : #include <string.h>
      21             : #include <stdio.h>
      22             : 
      23             : #include "libstephen/ht.h"
      24             : #include "libstephen/hta.h"
      25             : 
      26             : /*******************************************************************************
      27             : 
      28             :                                Private Functions
      29             : 
      30             : *******************************************************************************/
      31             : 
      32        1364 : unsigned int item_size(const smb_hta *obj)
      33             : {
      34        1364 :   return HTA_KEY_OFFSET + obj->key_size + obj->value_size;
      35             : }
      36             : 
      37        1356 : unsigned int convert_idx(const smb_hta *obj, unsigned int orig)
      38             : {
      39        1356 :   return orig * item_size(obj);
      40             : }
      41             : 
      42             : /**
      43             :    @brief Find the proper index for insertion into the table.
      44             :    @param obj Hash table object.
      45             :    @param key Key we're inserting.
      46             :  */
      47          84 : unsigned int hta_find_insert(const smb_hta *obj, void *key)
      48             : {
      49          84 :   unsigned int index = obj->hash(key) % obj->allocated;
      50          84 :   unsigned int bufidx = convert_idx(obj, index);
      51          84 :   unsigned int j = 1;
      52             : 
      53             :   // Continue searching until we either find a non-full slot, or we find the key
      54             :   // we're trying to insert.
      55             :   // until (cell.mark != full || cell.key == key)
      56             :   // while (cell.mark == full && cell.key != key)
      57         788 :   while (HTA_MARK(obj, bufidx) == HT_FULL &&
      58         310 :          obj->equal(key, obj->table + bufidx + HTA_KEY_OFFSET) != 0) {
      59             :     // This is quadratic probing, but I'm avoiding squaring numbers:
      60             :     // j:     1, 3, 5, 7,  9, 11, ..
      61             :     // index: 0, 1, 4, 9, 16, 25, 36
      62         310 :     index = (index + j) % obj->allocated;
      63         310 :     j += 2;
      64         310 :     bufidx = convert_idx(obj, index);
      65             :   }
      66             : 
      67          84 :   return index;
      68             : }
      69             : 
      70             : /**
      71             :    @brief Find the proper index for retrieval from the table.
      72             :    @param obj Hash table object.
      73             :    @param key Key we're looking up.
      74             :  */
      75         158 : unsigned int hta_find_retrieve(const smb_hta *obj, void *key)
      76             : {
      77         158 :   unsigned int index = obj->hash(key) % obj->allocated;
      78         158 :   unsigned int bufidx = convert_idx(obj, index);
      79         158 :   unsigned int j = 1;
      80             : 
      81             :   // Continue searching until we either find an empty slot, or we find the key
      82             :   // we're trying to insert.
      83             :   // until (cell.mark == empty || cell.key == key)
      84             :   // while (cell.mark != empty && cell.key != key)
      85        1389 :   while (HTA_MARK(obj, bufidx) != HT_EMPTY &&
      86         573 :          obj->equal(key, obj->table + bufidx + HTA_KEY_OFFSET) != 0) {
      87             :     // This is quadratic probing, but I'm avoiding squaring numbers:
      88             :     // j:     1, 3, 5, 7,  9, 11, ..
      89             :     // index: 0, 1, 4, 9, 16, 25, 36
      90         500 :     index = (index + j) % obj->allocated;
      91         500 :     j += 2;
      92         500 :     bufidx = convert_idx(obj, index);
      93             :   }
      94             : 
      95         158 :   return index;
      96             : }
      97             : 
      98             : /**
      99             :    @brief Expand the hash table, adding increment to the capacity of the table.
     100             : 
     101             :    @param table The table to expand.
     102             :  */
     103           2 : void hta_resize(smb_hta *table)
     104             : {
     105             :   void *old_table;
     106             :   unsigned int index, old_allocated, bufidx;
     107             : 
     108             :   // Step one: allocate new space for the table
     109           2 :   old_table = table->table;
     110           2 :   old_allocated = table->allocated;
     111           2 :   table->length = 0;
     112           2 :   table->allocated = ht_next_size(old_allocated);
     113           2 :   table->table = calloc(table->allocated, item_size(table));
     114             : 
     115             :   // Step two, add the old items to the new table.
     116          64 :   for (index = 0; index < old_allocated; index++) {
     117          62 :     bufidx = convert_idx(table, index);
     118          62 :     if (((int8_t*)old_table)[bufidx] == HT_FULL) {
     119          32 :       hta_insert(table, old_table + bufidx + HTA_KEY_OFFSET,
     120          32 :                  old_table + bufidx + HTA_KEY_OFFSET + table->value_size);
     121             :     }
     122             :   }
     123             : 
     124             :   // Step three: free old data.
     125           2 :   smb_free(old_table);
     126           2 : }
     127             : 
     128             : /**
     129             :    @brief Return the load factor of a hash table.
     130             : 
     131             :    @param table The table to find the load factor of.
     132             :    @returns The load factor of the hash table.
     133             :  */
     134          87 : double hta_load_factor(smb_hta *table)
     135             : {
     136          87 :   return ((double) table->length) / ((double) table->allocated);
     137             : }
     138             : 
     139             : /*******************************************************************************
     140             : 
     141             :                            Public Interface Functions
     142             : 
     143             : *******************************************************************************/
     144             : 
     145           6 : void hta_init(smb_hta *table, HTA_HASH hash_func, HTA_COMP equal,
     146             :               unsigned int key_size, unsigned int value_size)
     147             : {
     148             :   // Initialize values
     149           6 :   table->length = 0;
     150           6 :   table->allocated = HASH_TABLE_INITIAL_SIZE;
     151           6 :   table->key_size = key_size;
     152           6 :   table->value_size = value_size;
     153           6 :   table->hash = hash_func;
     154           6 :   table->equal = equal;
     155             : 
     156             :   // Allocate table
     157           6 :   table->table = calloc(HASH_TABLE_INITIAL_SIZE, item_size(table));
     158           6 : }
     159             : 
     160           6 : smb_hta *hta_create(HTA_HASH hash_func, HTA_COMP equal,
     161             :                     unsigned int key_size, unsigned int value_size)
     162             : {
     163             :   // Allocate and create the table.
     164             :   smb_hta *table;
     165           6 :   table = smb_new(smb_hta, 1);
     166           6 :   hta_init(table, hash_func, equal, key_size, value_size);
     167           6 :   return table;
     168             : }
     169             : 
     170           6 : void hta_destroy(smb_hta *table)
     171             : {
     172           6 :   smb_free(table->table);
     173           6 : }
     174             : 
     175           6 : void hta_delete(smb_hta *table)
     176             : {
     177           6 :   if (!table) {
     178           6 :     return;
     179             :   }
     180             : 
     181           6 :   hta_destroy(table);
     182           6 :   smb_free(table);
     183             : }
     184             : 
     185          87 : void hta_insert(smb_hta *table, void *key, void *value)
     186             : {
     187             :   unsigned int index, bufidx;
     188          87 :   if (hta_load_factor(table) > HASH_TABLE_MAX_LOAD_FACTOR) {
     189           2 :     hta_resize(table);
     190             :   }
     191             : 
     192             :   // First, probe for the key as if we're trying to return it.  If we find it,
     193             :   // we update the existing key.
     194          87 :   index = hta_find_retrieve(table, key);
     195          87 :   bufidx = convert_idx(table, index);
     196          87 :   if (HTA_MARK(table, bufidx) == HT_FULL) {
     197           3 :     memcpy(table->table + bufidx + HTA_KEY_OFFSET + table->key_size, value,
     198           3 :            table->value_size);
     199          90 :     return;
     200             :   }
     201             : 
     202             :   // If we don't find the key, then we find the first open slot or gravestone.
     203          84 :   index = hta_find_insert(table, key);
     204          84 :   bufidx = convert_idx(table, index);
     205          84 :   HTA_MARK(table, bufidx) = HT_FULL;
     206          84 :   memcpy(table->table + bufidx + HTA_KEY_OFFSET, key, table->key_size);
     207          84 :   memcpy(table->table + bufidx + HTA_KEY_OFFSET + table->key_size, value,
     208          84 :          table->value_size);
     209          84 :   table->length++;
     210             : }
     211             : 
     212           8 : void hta_remove(smb_hta *table, void *key, smb_status *status)
     213             : {
     214           8 :   *status = SMB_SUCCESS;
     215           8 :   unsigned int index = hta_find_retrieve(table, key);
     216           8 :   unsigned int bufidx = convert_idx(table, index);
     217             : 
     218             :   // If the returned slot isn't full, that means we couldn't find it.
     219           8 :   if (HTA_MARK(table, bufidx) != HT_FULL) {
     220           0 :     *status = SMB_NOT_FOUND_ERROR;
     221           8 :     return;
     222             :   }
     223             : 
     224             :   // Mark the slot with a "grave stone", indicating it is deleted.
     225           8 :   HTA_MARK(table, bufidx) = HT_GRAVE;
     226           8 :   table->length--;
     227             : }
     228             : 
     229          63 : void *hta_get(smb_hta const *table, void *key, smb_status *status)
     230             : {
     231          63 :   *status = SMB_SUCCESS;
     232          63 :   unsigned int index = hta_find_retrieve(table, key);
     233          63 :   unsigned int bufidx = convert_idx(table, index);
     234             : 
     235             :   // If the slot is not marked full, we didn't find the key.
     236          63 :   if (HTA_MARK(table, bufidx) != HT_FULL) {
     237           6 :     *status = SMB_NOT_FOUND_ERROR;
     238           6 :     return NULL;
     239             :   }
     240             : 
     241             :   // Otherwise, return the value.
     242          57 :   return table->table + bufidx + HTA_KEY_OFFSET + table->key_size;
     243             : }
     244             : 
     245          10 : bool hta_contains(smb_hta const *table, void *key)
     246             : {
     247          10 :   smb_status status = SMB_SUCCESS;
     248          10 :   hta_get(table, key, &status);
     249          10 :   return status == SMB_SUCCESS;
     250             : }
     251             : 
     252          67 : unsigned int hta_string_hash(void *data)
     253             : {
     254          67 :   char *theString = *(char**)data;
     255          67 :   unsigned int hash = 0;
     256             : 
     257         763 :   while (theString && *theString != '\0' ) {
     258         629 :     hash = (hash << 5) - hash + *theString;
     259         629 :     theString++;
     260             :   }
     261             : 
     262             :   // printf("Hash of \"%s\": %u\n", (char*)data.data_ptr, hash);
     263          67 :   return hash;
     264             : }
     265             : 
     266          36 : int hta_string_comp(void *left, void *right)
     267             : {
     268          36 :   char **l = (char**)left, **r = (char**)right;
     269          36 :   return strcmp(*l,*r);
     270             : }
     271             : 
     272         847 : int hta_int_comp(void *left, void *right)
     273             : {
     274         847 :   int *l = (int*)left, *r = (int*)right;
     275         847 :   return *l - *r;
     276             : }
     277             : 
     278           0 : void hta_print(FILE* f, smb_hta const *table, HTA_PRINT key, HTA_PRINT value,
     279             :                int full_mode)
     280             : {
     281             :   unsigned int i, bufidx;
     282           0 :   char *MARKS[] = {"EMPTY", " FULL", "GRAVE"};
     283             : 
     284           0 :   for (i = 0; i < table->allocated; i++) {
     285           0 :     bufidx = convert_idx(table, i);
     286           0 :     int8_t mark = HTA_MARK(table, bufidx);
     287           0 :     if (full_mode || mark == HT_FULL) {
     288           0 :       printf("[%04d|%05d|%s]:\n", i, bufidx, MARKS[mark]);
     289           0 :       if (mark == HT_FULL) {
     290           0 :         printf("  key: ");
     291           0 :         key(f, table->table + bufidx + HTA_KEY_OFFSET);
     292           0 :         printf("\n  value: ");
     293           0 :         value(f, table->table + bufidx + HTA_KEY_OFFSET + table->value_size);
     294           0 :         printf("\n");
     295             :       }
     296             :     }
     297             :   }
     298           0 : }

Generated by: LCOV version 1.11