/[hurd]/hurd-l4/libhurd-ihash/ihash.c
ViewVC logotype

Diff of /hurd-l4/libhurd-ihash/ihash.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.4 by marcus, Sun Aug 17 05:14:48 2003 UTC revision 1.5 by marcus, Tue Aug 26 22:47:18 2003 UTC
# Line 30  Line 30 
30  #include <hurd/ihash.h>  #include <hurd/ihash.h>
31    
32    
33  /* The odd prime numbers greater than twice the last and less than  /* The prime numbers of the form 4 * i + 3 for some i, all greater
34     2^40 (nobody needs more than 640 GB of memory).  */     than twice the previous one and smaller than 2^40 (for now).  */
35  static const uint64_t ihash_sizes[] =  static const uint64_t ihash_sizes[] =
36  {  {
37    3,    3,
38    7,    7,
39    17,    19,
40    37,    43,
41    79,    103,
42    163,    211,
43    331,    431,
44    673,    863,
45    1361,    1747,
46    2729,    3499,
47    5471,    7019,
48    10949,    14051,
49    21911,    28111,
50    43853,    56239,
51    87719,    112507,
52    175447,    225023,
53    350899,    450067,
54    701819,    900139,
55    1403641,    1800311,
56    2807303,    3600659,
57    5614657,    7201351,
58    11229331,    14402743,
59    22458671,    28805519,
60    44917381,    57611039,
61    89834777,    115222091,
62    179669557,    230444239,
63    359339171,    460888499,
64    718678369,    921777067,
65    1437356741,    1843554151,
66    UINT64_C (2874713497),    UINT64_C (3687108307),
67    UINT64_C (5749427029),    UINT64_C (7374216631),
68    UINT64_C (11498854069),    UINT64_C (14748433279),
69    UINT64_C (22997708177),    UINT64_C (29496866579),
70    UINT64_C (45995416409),    UINT64_C (58993733159),
71    UINT64_C (91990832831),    UINT64_C (117987466379),
72    UINT64_C (183981665689),    UINT64_C (235974932759),
73    UINT64_C (367963331389),    UINT64_C (471949865531),
74    UINT64_C (735926662813)    UINT64_C (943899731087)
75  };  };
76    
77    
# Line 79  static const unsigned int ihash_nsizes = Line 79  static const unsigned int ihash_nsizes =
79                                            / sizeof ihash_sizes[0]);                                            / sizeof ihash_sizes[0]);
80    
81    
 /* Return an initial index in the hash table HT for the key KEY, to  
    search for an entry.  */  
 #define HASH(ht, key)           ((key) % (ht)->size)  
   
   
 /* Return the next possible index in the hash table HT for the key  
    KEY, given the previous one.  */  
 #define REHASH(ht, key, idx)    (((idx) + (key)) % (ht)->size)  
   
   
82  /* Return 1 if the slot with the index IDX in the hash table HT is  /* Return 1 if the slot with the index IDX in the hash table HT is
83     empty, and 0 otherwise.  */     empty, and 0 otherwise.  */
84  static inline int  static inline int
85  index_empty (hurd_ihash_t ht, unsigned int idx)  index_empty (hurd_ihash_t ht, unsigned int idx)
86  {  {
87    return ht->table[idx] == _HURD_IHASH_EMPTY    return ht->items[idx].value == _HURD_IHASH_EMPTY
88      || ht->table[idx] == _HURD_IHASH_DELETED;      || ht->items[idx].value == _HURD_IHASH_DELETED;
89  }  }
90    
91    
# Line 104  index_empty (hurd_ihash_t ht, unsigned i Line 94  index_empty (hurd_ihash_t ht, unsigned i
94  static inline int  static inline int
95  index_valid (hurd_ihash_t ht, unsigned int idx, hurd_ihash_key_t key)  index_valid (hurd_ihash_t ht, unsigned int idx, hurd_ihash_key_t key)
96  {  {
97    return !index_empty (ht, idx) && ht->keys[idx] == key;    return !index_empty (ht, idx) && ht->items[idx].key == key;
98  }  }
99    
100    
# Line 115  static inline int Line 105  static inline int
105  find_index (hurd_ihash_t ht, hurd_ihash_key_t key)  find_index (hurd_ihash_t ht, hurd_ihash_key_t key)
106  {  {
107    unsigned int idx;    unsigned int idx;
108    unsigned int first_idx;    unsigned int i;
109      unsigned int up_idx;
110    first_idx = HASH (ht, key);    unsigned int down_idx;
111    idx = first_idx;  
112      idx = key % ht->size;
113    
114      if (ht->items[idx].value == _HURD_IHASH_EMPTY || ht->items[idx].key == key)
115        return idx;
116    
117      /* Instead of calculating idx + 1, idx + 4, idx + 9, ..., idx + i^2,
118         we add 1, 3, 5, 7, etc to the previous index.  We do this in both
119         directions separately.  */
120      i = 1;
121      up_idx = idx;
122      down_idx = idx;
123    
124    while (ht->table[idx] != _HURD_IHASH_EMPTY && ht->keys[idx] != key)    do
125      {      {
126        idx = REHASH (ht, key, idx);        up_idx = (up_idx + i) % ht->size;
127        if (idx == first_idx)        if (ht->items[up_idx].value == _HURD_IHASH_EMPTY
128          break;            || ht->items[up_idx].key == key)
129            return up_idx;
130    
131          if (down_idx < i)
132            down_idx += ht->size;
133          down_idx = (down_idx - i) % ht->size;
134          if (ht->items[down_idx].value == _HURD_IHASH_EMPTY
135              || ht->items[down_idx].key == key)
136            return down_idx;
137    
138          /* After (ht->size - 1) / 2 iterations, this will be 0.  */
139          i = (i + 2) % ht->size;
140      }      }
141      while (i);
142    
143      /* If we end up here, the item could not be found.  Return any
144         invalid index.  */
145    return idx;    return idx;
146  }  }
147    
148    
149    /* Remove the entry pointed to by the location pointer LOCP from the
150       hashtable HT.  LOCP is the location pointer of which the address
151       was provided to hurd_ihash_add().  */
152    static inline void
153    locp_remove (hurd_ihash_t ht, hurd_ihash_locp_t locp)
154    {
155      if (ht->cleanup)
156        (*ht->cleanup) (*locp, ht->cleanup_data);
157      *locp = _HURD_IHASH_DELETED;
158      ht->nr_items--;
159    }
160    
161    
162  /* Construction and destruction of hash tables.  */  /* Construction and destruction of hash tables.  */
163    
164  /* Initialize the hash table at address HT.  */  /* Initialize the hash table at address HT.  */
165  void  void
166  hurd_ihash_init (hurd_ihash_t ht)  hurd_ihash_init (hurd_ihash_t ht, off_t locp_offs)
167  {  {
168      ht->nr_items = 0;
169    ht->size = 0;    ht->size = 0;
170      ht->locp_offset = locp_offs;
171      ht->max_load = HURD_IHASH_MAX_LOAD_DEFAULT;
172    ht->cleanup = 0;    ht->cleanup = 0;
173  }  }
174    
# Line 158  hurd_ihash_destroy (hurd_ihash_t ht) Line 189  hurd_ihash_destroy (hurd_ihash_t ht)
189      }      }
190    
191    if (ht->size > 0)    if (ht->size > 0)
192      {      free (ht->items);
       free (ht->table);  
       free (ht->keys);  
       free (ht->locps);  
     }  
193  }  }
194    
195    
196  /* Create a hash table, initialize it and return it in HT.  If a  /* Create a hash table, initialize it and return it in HT.  If a
197     memory allocation error occurs, ENOMEM is returned, otherwise 0.  */     memory allocation error occurs, ENOMEM is returned, otherwise 0.  */
198  error_t  error_t
199  hurd_ihash_create (hurd_ihash_t *ht)  hurd_ihash_create (hurd_ihash_t *ht, off_t locp_offs)
200  {  {
201    *ht = malloc (sizeof (struct hurd_ihash));    *ht = malloc (sizeof (struct hurd_ihash));
202    if (*ht == NULL)    if (*ht == NULL)
203      return ENOMEM;      return ENOMEM;
204    
205    hurd_ihash_init (*ht);    hurd_ihash_init (*ht, locp_offs);
206    
207    return 0;    return 0;
208  }  }
# Line 203  hurd_ihash_set_cleanup (hurd_ihash_t ht, Line 230  hurd_ihash_set_cleanup (hurd_ihash_t ht,
230  }  }
231    
232    
233    /* Set the maximum load factor in percent to MAX_LOAD, which
234       should be between 1 and 100.  The default is
235       HURD_IHASH_MAX_LOAD_DEFAULT.  New elements are only added to the
236       hash table while the number of hashed elements is that much percent
237       of the total size of the hash table.  If more elements are added,
238       the hash table is first expanded and reorganized.  A MAX_LOAD of
239       100 will always fill the whole table before enlarging it, but note
240       that this will increase the cost of operations significantly when
241       the table is almost full.
242    
243       If the value is set to a smaller value than the current load
244       factor, the next reorganization will happen when a new item is
245       added to the hash table.  */
246    void
247    hurd_ihash_set_max_load (hurd_ihash_t ht, unsigned int max_load)
248    {
249      ht->max_load = max_load;
250    }
251    
252    
253  /* Helper function for hurd_ihash_add.  Return 1 if the item was  /* Helper function for hurd_ihash_add.  Return 1 if the item was
254     added, and 0 if it could not be added because no empty slot was     added, and 0 if it could not be added because no empty slot was
255     found.  The arguments are identical to hurd_ihash_add.  */     found.  The arguments are identical to hurd_ihash_add.
256    
257       We are using open address hashing.  As the hash function we use the
258       division method with quadratic probe.  This is guaranteed to try
259       all slots in the hash table if the prime number is 3 mod 4.  */
260  static inline int  static inline int
261  add_one (hurd_ihash_t ht, hurd_ihash_key_t key,  add_one (hurd_ihash_t ht, hurd_ihash_key_t key, hurd_ihash_value_t value)
          hurd_ihash_value_t item, hurd_ihash_locp_t *locp)  
262  {  {
263    unsigned int idx;    unsigned int idx;
   unsigned int first_idx;  
264    unsigned int first_free;    unsigned int first_free;
265    
266    first_idx = HASH (ht, key);    idx = key % ht->size;
267    idx = first_idx;    first_free = idx;
   first_free = first_idx;  
   
   /* Search for for an empty or deleted space.  Even if a deleted  
      space is found, keep going until we know if the same key  
      already exists.  */  
   while (ht->table[idx] != _HURD_IHASH_EMPTY  
          && ht->keys[idx] != key)  
     {  
       if (ht->table[idx] == _HURD_IHASH_DELETED)  
         first_free = idx;  
         
       idx = REHASH (ht, key, idx);  
       if (idx == first_idx)  
         break;  
     }  
268    
269    if (ht->keys[idx] == key)    if (ht->items[idx].value != _HURD_IHASH_EMPTY && ht->items[idx].key != key)
270      {      {
271        if (ht->cleanup)        /* Instead of calculating idx + 1, idx + 4, idx + 9, ..., idx +
272          ht->cleanup (ht->table[idx], ht->cleanup_data);           i^2, we add 1, 3, 5, 7, ... 2 * i - 1 to the previous index.
273        ht->table[idx] = _HURD_IHASH_DELETED;           We do this in both directions separately.  */
274          unsigned int i = 1;
275          unsigned int up_idx = idx;
276          unsigned int down_idx = idx;
277    
278          do
279            {
280              up_idx = (up_idx + i) % ht->size;
281              if (ht->items[up_idx].value == _HURD_IHASH_EMPTY
282                  || ht->items[up_idx].key == key)
283                {
284                  idx = up_idx;
285                  break;
286                }
287              if (first_free == idx
288                  && ht->items[up_idx].value == _HURD_IHASH_DELETED)
289                first_free = up_idx;
290    
291              if (down_idx < i)
292                down_idx += ht->size;
293              down_idx = (down_idx - i) % ht->size;
294              if (down_idx < 0)
295                down_idx += ht->size;
296              else
297                down_idx %= ht->size;
298              if (ht->items[down_idx].value == _HURD_IHASH_EMPTY
299                  || ht->items[down_idx].key == key)
300                {
301                  idx = down_idx;
302                  break;
303                }
304              if (first_free == idx
305                  && ht->items[down_idx].value == _HURD_IHASH_DELETED)
306                first_free = down_idx;
307    
308              /* After (ht->size - 1) / 2 iterations, this will be 0.  */
309              i = (i + 2) % ht->size;
310            }
311          while (i);
312      }      }
313    
314      /* Remove the old entry for this key if necessary.  */
315      if (index_valid (ht, idx, key))
316        locp_remove (ht, &ht->items[idx].value);
317    
318    /* If we have not found an empty slot, maybe the last one we    /* If we have not found an empty slot, maybe the last one we
319       looked at was empty (or just got deleted).  */       looked at was empty (or just got deleted).  */
320    if (!index_empty (ht, first_free))    if (!index_empty (ht, first_free))
# Line 246  add_one (hurd_ihash_t ht, hurd_ihash_key Line 322  add_one (hurd_ihash_t ht, hurd_ihash_key
322    
323    if (index_empty (ht, first_free))    if (index_empty (ht, first_free))
324      {      {
325        ht->table[first_free] = item;        ht->nr_items++;
326        ht->keys[first_free] = key;        ht->items[first_free].value = value;
327        ht->locps[first_free] = locp;        ht->items[first_free].key = key;
328          
329        if (locp)        if (ht->locp_offset != HURD_IHASH_NO_LOCP)
330          *locp = &ht->table[first_free];          *((hurd_ihash_locp_t) (((char *) value) + ht->locp_offset))
331                    = &ht->items[first_free].value;
332    
333        return 1;        return 1;
334      }      }
335    
# Line 260  add_one (hurd_ihash_t ht, hurd_ihash_key Line 337  add_one (hurd_ihash_t ht, hurd_ihash_key
337  }  }
338    
339        
340  /* Add ITEM to the hash table HT under the key KEY.  LOCP is the  /* Add ITEM to the hash table HT under the key KEY.  If there already
341     address of a location pointer in ITEM; If non-NULL, it will be     is an item under this key, call the cleanup function (if any) for
342     filled with a pointer that may be used as an argument to     it before overriding the value.  If a memory allocation error
343     hurd_ihash_locp_remove().  (The variable pointed to by LOCP may be     occurs, ENOMEM is returned, otherwise 0.  */
    written to subsequently between this call and when the element is  
    deleted).  If there already is an item under this key, call the  
    cleanup function (if any) for it before overriding the value.  If a  
    memory allocation error occurs, ENOMEM is returned, otherwise 0.  */  
344  error_t  error_t
345  hurd_ihash_add (hurd_ihash_t ht, hurd_ihash_key_t key,  hurd_ihash_add (hurd_ihash_t ht, hurd_ihash_key_t key, hurd_ihash_value_t item)
                 hurd_ihash_value_t item, hurd_ihash_locp_t *locp)  
346  {  {
347    struct hurd_ihash old_ht = *ht;    struct hurd_ihash old_ht = *ht;
348    int was_added;    int was_added;
349    int i;    int i;
350    
351    if (ht->size && add_one (ht, key, item, locp))    if (ht->size)
352      return 0;      {
353          /* Only fill the hash table up to its maximum load factor.  */
354          if (ht->nr_items * 100 / ht->size <= ht->max_load)
355            if (add_one (ht, key, item))
356              return 0;
357        }
358    
359    /* The hash table is too small, and we have to increase it.  */    /* The hash table is too small, and we have to increase it.  */
360    for (i = 0; i < ihash_nsizes; i++)    for (i = 0; i < ihash_nsizes; i++)
361      if (ihash_sizes[i] > old_ht.size)      if (ihash_sizes[i] > old_ht.size)
362        break;        break;
363    if (i == ihash_nsizes    if (i == ihash_nsizes
364        || ihash_sizes[i] > SIZE_MAX / sizeof (hurd_ihash_value_t)        || ihash_sizes[i] > SIZE_MAX / sizeof (struct _hurd_ihash_item))
       || ihash_sizes[i] > SIZE_MAX / sizeof (hurd_ihash_key_t)  
       || ihash_sizes[i] > SIZE_MAX / sizeof (hurd_ihash_locp_t *))  
365      return ENOMEM;              /* Surely will be true momentarily.  */      return ENOMEM;              /* Surely will be true momentarily.  */
366        
367      ht->nr_items = 0;
368    ht->size = ihash_sizes[i];    ht->size = ihash_sizes[i];
369    ht->table = malloc (ht->size * sizeof (hurd_ihash_value_t));    /* calloc() will initialize all values to _HURD_IHASH_EMPTY implicitely.  */
370    ht->keys = malloc (ht->size * sizeof (hurd_ihash_key_t));    ht->items = calloc (ht->size, sizeof (struct _hurd_ihash_item));
   ht->locps = malloc (ht->size * sizeof (hurd_ihash_locp_t *));  
371    
372    if (ht->table == NULL || ht->locps == NULL || ht->keys == NULL)    if (ht->items == NULL)
373      {      {
374        if (ht->table)        if (ht->items)
375          free (ht->table);          free(ht->items);
       if (ht->keys)  
         free(ht->keys);  
       if (ht->locps)  
         free (ht->locps);  
376    
377        *ht = old_ht;        *ht = old_ht;
378        return ENOMEM;        return ENOMEM;
379      }      }
380    
   for (i = 0; i < ht->size; i++)  
     ht->table[i] = _HURD_IHASH_EMPTY;  
   
381    /* We have to rehash the old entries.  */    /* We have to rehash the old entries.  */
382    for (i = 0; i < old_ht.size; i++)    for (i = 0; i < old_ht.size; i++)
383      if (!index_empty (&old_ht, i))      if (!index_empty (&old_ht, i))
384        {        {
385          was_added = add_one (ht, old_ht.keys[i], old_ht.table[i],          was_added = add_one (ht, old_ht.items[i].key, old_ht.items[i].value);
                              old_ht.locps[i]);  
386          assert (was_added);          assert (was_added);
387        }        }
388    
389    /* Finally add the new element!  */    /* Finally add the new element!  */
390    was_added = add_one (ht, key, item, locp);    was_added = add_one (ht, key, item);
391    assert (was_added);    assert (was_added);
392    
393    if (old_ht.size > 0)    if (old_ht.size > 0)
394      {      free (old_ht.items);
       free (old_ht.table);  
       free (old_ht.keys);  
       free (old_ht.locps);  
     }  
395    
396    return 0;    return 0;
397  }  }
# Line 344  hurd_ihash_find (hurd_ihash_t ht, hurd_i Line 407  hurd_ihash_find (hurd_ihash_t ht, hurd_i
407    else    else
408      {      {
409        int idx = find_index (ht, key);        int idx = find_index (ht, key);
410        return index_valid (ht, idx, key) ? ht->table[idx] : NULL;        return index_valid (ht, idx, key) ? ht->items[idx].value : NULL;
411      }      }
412  }  }
413    
# Line 358  hurd_ihash_remove (hurd_ihash_t ht, hurd Line 421  hurd_ihash_remove (hurd_ihash_t ht, hurd
421    
422    if (index_valid (ht, idx, key))    if (index_valid (ht, idx, key))
423      {      {
424        hurd_ihash_locp_remove (ht, &ht->table[idx]);        locp_remove (ht, &ht->items[idx].value);
425        return 1;        return 1;
426      }      }
427    else    else
# Line 369  hurd_ihash_remove (hurd_ihash_t ht, hurd Line 432  hurd_ihash_remove (hurd_ihash_t ht, hurd
432  /* Remove the entry pointed to by the location pointer LOCP from the  /* Remove the entry pointed to by the location pointer LOCP from the
433     hashtable HT.  LOCP is the location pointer of which the address     hashtable HT.  LOCP is the location pointer of which the address
434     was provided to hurd_ihash_add().  This call is faster than     was provided to hurd_ihash_add().  This call is faster than
435     hurd_ihash_remove().  HT can be NULL, in which case the call still     hurd_ihash_remove().  */
    succeeds, but the cleanup function (if any) will not be invoked in  
    this case.  */  
436  void  void
437  hurd_ihash_locp_remove (hurd_ihash_t ht, hurd_ihash_locp_t locp)  hurd_ihash_locp_remove (hurd_ihash_t ht, hurd_ihash_locp_t locp)
438  {  {
439    if (ht && ht->cleanup)    locp_remove (ht, locp);
     (*ht->cleanup) (*locp, ht->cleanup_data);  
   *locp = _HURD_IHASH_DELETED;  
440  }  }

Legend:
Removed from v.1.4  
changed lines
  Added in v.1.5

savannah-hackers-public@gnu.org
ViewVC Help
Powered by ViewVC 1.1.26