Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * This file is part of libdom.
  3.  * Licensed under the MIT License,
  4.  *                      http://www.opensource.org/licenses/mit-license.php
  5.  * Copyright 2006 Rob Kendrick <rjek@rjek.com>
  6.  * Copyright 2006 Richard Wilson <info@tinct.net>
  7.  * Copyright 2009 Bo Yang <struggleyb.nku@gmail.com>
  8.  */
  9.  
  10.  
  11.  
  12. typedef unsigned char uint8_t;
  13. typedef unsigned short uint16_t;
  14. typedef unsigned int uint32_t;
  15.  
  16. #include <stdlib.h>
  17. #include <string.h>
  18. #include <stdbool.h>
  19. #include <assert.h>
  20. #ifdef TEST_RIG
  21. #include <stdio.h>
  22. #endif
  23. #include "utils/hashtable.h"
  24.  
  25. #include <libwapcaplet/libwapcaplet.h>
  26.  
  27. /* The hash table entry */
  28. struct _dom_hash_entry {
  29.         void *key;                      /**< The key pointer */
  30.         void *value;                    /**< The value pointer */
  31.         struct _dom_hash_entry *next;   /**< Next entry */
  32. };
  33.  
  34. /* The hash table */
  35. struct dom_hash_table {
  36.         const dom_hash_vtable *vtable;  /**< Vtable */
  37.         void *pw;                       /**< Client data */
  38.         unsigned int nchains;           /**< Number of chains */
  39.         struct _dom_hash_entry **chain; /**< The chain head */
  40.         uint32_t nentries;              /**< The entries in this table */
  41. };
  42.  
  43.  
  44. /**
  45.  * Create a new hash table, and return a context for it.  The memory consumption
  46.  * of a hash table is approximately 8 + (nchains * 12) bytes if it is empty.
  47.  *
  48.  * \param chains  Number of chains/buckets this hash table will have.  This
  49.  *                should be a prime number, and ideally a prime number just
  50.  *                over a power of two, for best performance and distribution
  51.  * \param vtable  Client vtable
  52.  * \param pw      Client private data
  53.  * \return struct dom_hash_table containing the context of this hash table or
  54.  *         NULL if there is insufficent memory to create it and its chains.
  55.  */
  56. dom_hash_table *_dom_hash_create(unsigned int chains,
  57.                 const dom_hash_vtable *vtable, void *pw)
  58. {
  59.         dom_hash_table *r = malloc(sizeof(struct dom_hash_table));
  60.         if (r == NULL) {
  61.                 return NULL;
  62.         }
  63.  
  64.         r->vtable = vtable;
  65.         r->pw = pw;
  66.         r->nentries = 0;
  67.         r->nchains = chains;
  68.         r->chain = calloc(chains, sizeof(struct _dom_hash_entry *));
  69.         if (r->chain == NULL) {
  70.                 free(r);
  71.                 return NULL;
  72.         }
  73.  
  74.         return r;
  75. }
  76.  
  77. /**
  78.  * Clone a hash table.
  79.  *
  80.  * \param ht        Hash table to clone.
  81.  *
  82.  * \return The cloned hash table.
  83.  */
  84. dom_hash_table *_dom_hash_clone(dom_hash_table *ht)
  85. {
  86.         void *key = NULL, *nkey = NULL;
  87.         void *value = NULL, *nvalue = NULL;
  88.         uintptr_t c1, *c2 = NULL;
  89.         struct dom_hash_table *ret;
  90.        
  91.         ret = _dom_hash_create(ht->nchains, ht->vtable, ht->pw);
  92.         if (ret == NULL)
  93.                 return NULL;
  94.  
  95.         while ( (key = _dom_hash_iterate(ht, &c1, &c2)) != NULL) {
  96.                 nkey = ht->vtable->clone_key(key, ht->pw);
  97.                 if (nkey == NULL) {
  98.                         _dom_hash_destroy(ret);
  99.                         return NULL;
  100.                 }
  101.  
  102.                 value = _dom_hash_get(ht, key);
  103.                 nvalue = ht->vtable->clone_value(value, ht->pw);
  104.                 if (nvalue == NULL) {
  105.                         ht->vtable->destroy_key(nkey, ht->pw);
  106.                         _dom_hash_destroy(ret);
  107.                         return NULL;
  108.                 }
  109.  
  110.                 if (_dom_hash_add(ret, nkey, nvalue, false) == false) {
  111.                         _dom_hash_destroy(ret);
  112.                         return NULL;
  113.                 }
  114.         }
  115.  
  116.         return ret;
  117. }
  118.  
  119. /**
  120.  * Destroys a hash table, freeing all memory associated with it.
  121.  *
  122.  * \param ht        Hash table to destroy. After the function returns, this
  123.  *                  will nolonger be valid
  124.  */
  125. void _dom_hash_destroy(dom_hash_table *ht)
  126. {
  127.         unsigned int i;
  128.  
  129.         if (ht == NULL)
  130.                 return;
  131.  
  132.         for (i = 0; i < ht->nchains; i++) {
  133.                 if (ht->chain[i] != NULL) {
  134.                         struct _dom_hash_entry *e = ht->chain[i];
  135.                         while (e) {
  136.                                 struct _dom_hash_entry *n = e->next;
  137.                                 ht->vtable->destroy_key(e->key, ht->pw);
  138.                                 ht->vtable->destroy_value(e->value, ht->pw);
  139.                                 free(e);
  140.                                 e = n;
  141.                         }
  142.                 }
  143.         }
  144.  
  145.         free(ht->chain);
  146.         free(ht);
  147. }
  148.  
  149. /**
  150.  * Adds a key/value pair to a hash table
  151.  *
  152.  * \param  ht     The hash table context to add the key/value pair to.
  153.  * \param  key    The key to associate the value with.
  154.  * \param  value  The value to associate the key with.
  155.  * \return true if the add succeeded, false otherwise.  (Failure most likely
  156.  *         indicates insufficent memory to make copies of the key and value.
  157.  */
  158. bool _dom_hash_add(dom_hash_table *ht, void *key, void *value,
  159.                 bool replace)
  160. {
  161.         unsigned int h, c;
  162.         struct _dom_hash_entry *e;
  163.  
  164.         if (ht == NULL || key == NULL || value == NULL)
  165.                 return false;
  166.  
  167.         h = ht->vtable->hash(key, ht->pw);
  168.         c = h % ht->nchains;
  169.  
  170.         for (e = ht->chain[c]; e; e = e->next) {
  171.                 if (ht->vtable->key_isequal(key, e->key, ht->pw)) {
  172.                         if (replace == true) {
  173.                                 e->value = value;
  174.                                 return true;
  175.                         } else {
  176.                                 return false;
  177.                         }
  178.                 }
  179.         }
  180.  
  181.         e = malloc(sizeof(struct _dom_hash_entry));
  182.         if (e == NULL) {
  183.                 return false;
  184.         }
  185.  
  186.         e->key = key;
  187.         e->value = value;
  188.  
  189.         e->next = ht->chain[c];
  190.         ht->chain[c] = e;
  191.         ht->nentries++;
  192.  
  193.         return true;
  194. }
  195.  
  196. /**
  197.  * Looks up a the value associated with with a key from a specific hash table.
  198.  *
  199.  * \param  ht   The hash table context to look up
  200.  * \param  key  The key to search for
  201.  * \return The value associated with the key, or NULL if it was not found.
  202.  */
  203. void *_dom_hash_get(struct dom_hash_table *ht, void *key)
  204. {
  205.         unsigned int h, c;
  206.         struct _dom_hash_entry *e;
  207.  
  208.         if (ht == NULL || key == NULL)
  209.                 return NULL;
  210.  
  211.         h = ht->vtable->hash(key, ht->pw);
  212.         c = h % ht->nchains;
  213.  
  214.         for (e = ht->chain[c]; e; e = e->next) {
  215.                 if (ht->vtable->key_isequal(key, e->key, ht->pw))
  216.                         return e->value;
  217.         }
  218.  
  219.         return NULL;
  220. }
  221.  
  222. /**
  223.  * Delete the key from the hashtable.
  224.  *
  225.  * \param ht   The hashtable object
  226.  * \param key  The key to delete
  227.  * \return The deleted value
  228.  */
  229. void *_dom_hash_del(struct dom_hash_table *ht, void *key)
  230. {
  231.         unsigned int h, c;
  232.         struct _dom_hash_entry *e, *p;
  233.         void *ret;
  234.  
  235.         if (ht == NULL || key == NULL)
  236.                 return NULL;
  237.  
  238.         h = ht->vtable->hash(key, ht->pw);
  239.         c = h % ht->nchains;
  240.  
  241.         p = ht->chain[c];
  242.         for (e = p; e; p = e, e = e->next) {
  243.                 if (ht->vtable->key_isequal(key, e->key, ht->pw)) {
  244.                         if (p != e) {
  245.                                 p->next = e->next;
  246.                         } else {
  247.                                 /* The first item in this chain is target*/
  248.                                 ht->chain[c] = e->next;
  249.                         }
  250.  
  251.                         ret = e->value;
  252.                         free(e);
  253.                         ht->nentries--;
  254.                         return ret;
  255.                 }
  256.         }
  257.        
  258.         return NULL;
  259. }
  260.  
  261. /**
  262.  * Iterate through all available hash keys.
  263.  *
  264.  * \param  ht  The hash table context to iterate.
  265.  * \param  c1  Pointer to first context
  266.  * \param  c2  Pointer to second context (set to 0 on first call)
  267.  * \return The next hash key, or NULL for no more keys
  268.  */
  269. void *_dom_hash_iterate(struct dom_hash_table *ht, unsigned long int *c1,
  270.                 unsigned long int **c2)
  271. {
  272.         struct _dom_hash_entry **he = (struct _dom_hash_entry **) c2;
  273.  
  274.         if (ht == NULL)
  275.                 return NULL;
  276.  
  277.         if (!*he)
  278.                 *c1 = -1;
  279.         else
  280.                 *he = (*he)->next;
  281.  
  282.         if (*he)
  283.                 return (*he)->key;
  284.  
  285.         while (!*he) {
  286.                 (*c1)++;
  287.                 if (*c1 >= ht->nchains)
  288.                         return NULL;
  289.                 *he = ht->chain[*c1];
  290.         }
  291.         return (*he)->key;
  292. }
  293.  
  294. /**
  295.  * Get the number of elements in this hash table
  296.  *
  297.  * \param ht  The hash table
  298.  *
  299.  * \return the number of elements
  300.  */
  301. uint32_t _dom_hash_get_length(struct dom_hash_table *ht)
  302. {
  303.         return ht->nentries;
  304. }
  305.  
  306. /*-----------------------------------------------------------------------*/
  307.  
  308. /* A simple test rig.  To compile, use:
  309.  * gcc -g  -o hashtest -I../ -I../../include  -DTEST_RIG  hashtable.c
  310.  *
  311.  * If you make changes to this hash table implementation, please rerun this
  312.  * test, and if possible, through valgrind to make sure there are no memory
  313.  * leaks or invalid memory accesses.  If you add new functionality, please
  314.  * include a test for it that has good coverage along side the other tests.
  315.  */
  316.  
  317. #ifdef TEST_RIG
  318.  
  319.  
  320. /**
  321.  * Hash a pointer, returning a 32bit value.  
  322.  *
  323.  * \param  ptr  The pointer to hash.
  324.  * \return the calculated hash value for the pointer.
  325.  */
  326.  
  327. static inline unsigned int _dom_hash_pointer_fnv(void *ptr)
  328. {
  329.         return (unsigned int) ptr;
  330. }
  331.  
  332. static void *test_alloc(void *p, size_t size, void *ptr)
  333. {
  334.         if (p != NULL) {
  335.                 free(p);
  336.                 return NULL;
  337.         }
  338.  
  339.         if (p == NULL) {
  340.                 return malloc(size);
  341.         }
  342. }
  343.  
  344. int main(int argc, char *argv[])
  345. {
  346.         struct dom_hash_table *a, *b;
  347.         FILE *dict;
  348.         char keybuf[BUFSIZ], valbuf[BUFSIZ];
  349.         int i;
  350.         char *cow="cow", *moo="moo", *pig="pig", *oink="oink",
  351.                         *chicken="chikcken", *cluck="cluck",
  352.                         *dog="dog", *woof="woof", *cat="cat",
  353.                         *meow="meow";
  354.         void *ret;
  355.  
  356.         a = _dom_hash_create(79, _dom_hash_pointer_fnv, test_alloc, NULL);
  357.         assert(a != NULL);
  358.  
  359.         b = _dom_hash_create(103, _dom_hash_pointer_fnv, test_alloc, NULL);
  360.         assert(b != NULL);
  361.  
  362.         _dom_hash_add(a, cow, moo ,true);
  363.         _dom_hash_add(b, moo, cow ,true);
  364.  
  365.         _dom_hash_add(a, pig, oink ,true);
  366.         _dom_hash_add(b, oink, pig ,true);
  367.  
  368.         _dom_hash_add(a, chicken, cluck ,true);
  369.         _dom_hash_add(b, cluck, chicken ,true);
  370.  
  371.         _dom_hash_add(a, dog, woof ,true);
  372.         _dom_hash_add(b, woof, dog ,true);
  373.  
  374.         _dom_hash_add(a, cat, meow ,true);
  375.         _dom_hash_add(b, meow, cat ,true);
  376.  
  377. #define MATCH(x,y) assert(!strcmp((char *)hash_get(a, x), (char *)y)); \
  378.                 assert(!strcmp((char *)hash_get(b, y), (char *)x))
  379.         MATCH(cow, moo);
  380.         MATCH(pig, oink);
  381.         MATCH(chicken, cluck);
  382.         MATCH(dog, woof);
  383.         MATCH(cat, meow);
  384.  
  385.         assert(hash_get_length(a) == 5);
  386.         assert(hash_get_length(b) == 5);
  387.  
  388.         _dom_hash_del(a, cat);
  389.         _dom_hash_del(b, meow);
  390.         assert(hash_get(a, cat) == NULL);
  391.         assert(hash_get(b, meow) == NULL);
  392.  
  393.         assert(hash_get_length(a) == 4);
  394.         assert(hash_get_length(b) == 4);
  395.  
  396.         _dom_hash_destroy(a, NULL, NULL);
  397.         _dom_hash_destroy(b, NULL, NULL);
  398.  
  399.         /* This test requires /usr/share/dict/words - a large list of English
  400.          * words.  We load the entire file - odd lines are used as keys, and
  401.          * even lines are used as the values for the previous line.  we then
  402.          * work through it again making sure everything matches.
  403.          *
  404.          * We do this twice - once in a hash table with many chains, and once
  405.          * with a hash table with fewer chains.
  406.          */
  407.  
  408.         a = _dom_hash_create(1031, _dom_hash_pointer_fnv, test_alloc, NULL);
  409.         b = _dom_hash_create(7919, _dom_hash_pointer_fnv, test_alloc, NULL);
  410.  
  411.         dict = fopen("/usr/share/dict/words", "r");
  412.         if (dict == NULL) {
  413.                 fprintf(stderr, "Unable to open /usr/share/dict/words - \
  414.                                 extensive testing skipped.\n");
  415.                 exit(0);
  416.         }
  417.  
  418.         while (!feof(dict)) {
  419.                 fscanf(dict, "%s", keybuf);
  420.                 fscanf(dict, "%s", valbuf);
  421.                 _dom_hash_add(a, keybuf, valbuf, true);
  422.                 _dom_hash_add(b, keybuf, valbuf, true);
  423.         }
  424.  
  425.         for (i = 0; i < 5; i++) {
  426.                 fseek(dict, 0, SEEK_SET);
  427.  
  428.                 while (!feof(dict)) {
  429.                         fscanf(dict, "%s", keybuf);
  430.                         fscanf(dict, "%s", valbuf);
  431.                         assert(strcmp(hash_get(a, keybuf), valbuf) == 0);
  432.                         assert(strcmp(hash_get(b, keybuf), valbuf) == 0);
  433.                 }
  434.         }
  435.  
  436.         _dom_hash_destroy(a, NULL, NULL);
  437.         _dom_hash_destroy(b, NULL, NULL);
  438.  
  439.         fclose(dict);
  440.  
  441.         return 0;
  442. }
  443.  
  444. #endif
  445.