Return void, not int.
[oweals/opkg-lede.git] / libopkg / hash_table.c
1 /* hash.c - hash tables for opkg
2
3    Steven M. Ayer, Jamey Hicks
4    
5    Copyright (C) 2002 Compaq Computer Corporation
6
7    This program is free software; you can redistribute it and/or
8    modify it under the terms of the GNU General Public License as
9    published by the Free Software Foundation; either version 2, or (at
10    your option) any later version.
11
12    This program is distributed in the hope that it will be useful, but
13    WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    General Public License for more details.
16 */
17
18 #include <errno.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include "hash_table.h"
23 #include "opkg_message.h"
24 #include "libbb/libbb.h"
25
26
27 static unsigned long
28 djb2_hash(const unsigned char *str)
29 {
30         unsigned long hash = 5381;
31         int c;
32         while ((c = *str++))
33                 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
34         return hash;
35 }
36
37 static int
38 hash_index(hash_table_t *hash, const char *key)
39 {
40         return djb2_hash((const unsigned char *)key) % hash->n_buckets;
41 }
42
43 /*
44  * this is an open table keyed by strings
45  */
46 void
47 hash_table_init(const char *name, hash_table_t *hash, int len)
48 {
49         if (hash->entries != NULL) {
50                 fprintf(stderr, "ERROR: %s called on a non empty hash table\n",
51                                 __FUNCTION__);
52                 return;
53         }
54
55         memset(hash, 0, sizeof(hash_table_t));
56
57         hash->name = name;
58         hash->n_buckets = len;
59         hash->entries = xcalloc(hash->n_buckets, sizeof(hash_entry_t));
60 }
61
62 void
63 hash_print_stats(hash_table_t *hash)
64 {
65         printf("hash_table: %s, %d bytes\n"
66                 "\tn_buckets=%d, n_elements=%d, n_collisions=%d\n"
67                 "\tmax_bucket_len=%d, n_used_buckets=%d, ave_bucket_len=%.2f\n"
68                 "\tn_hits=%d, n_misses=%d\n",
69                 hash->name,
70                 hash->n_buckets*(int)sizeof(hash_entry_t),
71                 hash->n_buckets,
72                 hash->n_elements,
73                 hash->n_collisions,
74                 hash->max_bucket_len,
75                 hash->n_used_buckets,
76                 (hash->n_used_buckets ?
77                         ((float)hash->n_elements)/hash->n_used_buckets : 0.0f),
78                 hash->n_hits,
79                 hash->n_misses);
80 }
81
82 void hash_table_deinit(hash_table_t *hash)
83 {
84     int i;
85     if (!hash)
86         return;
87
88     /* free the reminaing entries */
89     for (i = 0; i < hash->n_buckets; i++) {
90         hash_entry_t *hash_entry = (hash->entries + i);
91         free (hash_entry->key);
92         /* skip the first entry as this is part of the array */
93         hash_entry = hash_entry->next;
94         while (hash_entry)
95         {
96           hash_entry_t *old = hash_entry;
97           hash_entry = hash_entry->next;
98           free (old->key);
99           free (old);
100         }
101     }
102
103     free (hash->entries);
104
105     hash->entries = NULL;
106     hash->n_buckets = 0;
107 }
108
109 void *hash_table_get(hash_table_t *hash, const char *key)
110 {
111   int ndx= hash_index(hash, key);
112   hash_entry_t *hash_entry = hash->entries + ndx;
113   while (hash_entry) 
114   {
115     if (hash_entry->key) 
116     {
117       if (strcmp(key, hash_entry->key) == 0) {
118          // opkg_message(NULL, OPKG_DEBUG, "Function: %s. Key found for '%s' \n", __FUNCTION__, key);
119          hash->n_hits++;
120          return hash_entry->data;
121       }
122     }
123     hash_entry = hash_entry->next;
124   }
125   hash->n_misses++;
126   return NULL;
127 }
128
129 int hash_table_insert(hash_table_t *hash, const char *key, void *value)
130 {
131      int bucket_len = 0;
132      int ndx= hash_index(hash, key);
133      hash_entry_t *hash_entry = hash->entries + ndx;
134      if (hash_entry->key) {
135           if (strcmp(hash_entry->key, key) == 0) {
136                /* alread in table, update the value */
137                hash_entry->data = value;
138                return 0;
139           } else {
140                /* 
141                 * if this is a collision, we have to go to the end of the ll,
142                 * then add a new entry
143                 * before we can hook up the value
144                 */
145                while (hash_entry->next) {
146                     hash_entry = hash_entry->next;
147                     if (strcmp(hash_entry->key, key) == 0) {
148                         hash_entry->data = value;
149                         return 0;
150                     }
151                     bucket_len++;
152                }
153                hash_entry->next = xcalloc(1, sizeof(hash_entry_t));
154                hash_entry = hash_entry->next;
155                hash_entry->next = NULL;
156
157                hash->n_collisions++;
158                if (++bucket_len > hash->max_bucket_len)
159                     hash->max_bucket_len = bucket_len;
160           }
161      } else
162           hash->n_used_buckets++;
163
164      hash->n_elements++;
165      hash_entry->key = xstrdup(key);
166      hash_entry->data = value;
167
168      return 0;
169 }
170
171 int hash_table_remove(hash_table_t *hash, const char *key)
172 {
173     int ndx= hash_index(hash, key);
174     hash_entry_t *hash_entry = hash->entries + ndx;
175     hash_entry_t *next_entry=NULL, *last_entry=NULL;
176     while (hash_entry) 
177     {
178         if (hash_entry->key) 
179         {
180             if (strcmp(key, hash_entry->key) == 0) {
181                 free(hash_entry->key);
182                 if (last_entry) {
183                     last_entry->next = hash_entry->next;
184                     free(hash_entry);
185                 } else {
186                     next_entry = hash_entry->next;
187                     if (next_entry) {
188                         memmove(hash_entry, next_entry, sizeof(hash_entry_t));
189                         free(next_entry);
190                     } else {
191                         memset(hash_entry, 0 , sizeof(hash_entry_t));
192                     }
193                 }
194                 return 1;
195             }
196         }
197         last_entry = hash_entry;
198         hash_entry = hash_entry->next;
199     }
200     return 0;
201 }
202
203 void hash_table_foreach(hash_table_t *hash, void (*f)(const char *key, void *entry, void *data), void *data)
204
205     int i;
206     if (!hash || !f)
207         return;
208
209     for (i = 0; i < hash->n_buckets; i++) {
210         hash_entry_t *hash_entry = (hash->entries + i);
211         do {
212             if(hash_entry->key) {
213                 f(hash_entry->key, hash_entry->data, data);
214             }
215         } while((hash_entry = hash_entry->next));
216     }
217 }
218