Fix some errno abuse.
[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 <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include "hash_table.h"
22 #include "opkg_message.h"
23 #include "libbb/libbb.h"
24
25
26 static unsigned long
27 djb2_hash(const unsigned char *str)
28 {
29         unsigned long hash = 5381;
30         int c;
31         while ((c = *str++))
32                 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
33         return hash;
34 }
35
36 static int
37 hash_index(hash_table_t *hash, const char *key)
38 {
39         return djb2_hash((const unsigned char *)key) % hash->n_buckets;
40 }
41
42 /*
43  * this is an open table keyed by strings
44  */
45 void
46 hash_table_init(const char *name, hash_table_t *hash, int len)
47 {
48         if (hash->entries != NULL) {
49                 fprintf(stderr, "ERROR: %s called on a non empty hash table\n",
50                                 __FUNCTION__);
51                 return;
52         }
53
54         memset(hash, 0, sizeof(hash_table_t));
55
56         hash->name = name;
57         hash->n_buckets = len;
58         hash->entries = xcalloc(hash->n_buckets, sizeof(hash_entry_t));
59 }
60
61 void
62 hash_print_stats(hash_table_t *hash)
63 {
64         printf("hash_table: %s, %d bytes\n"
65                 "\tn_buckets=%d, n_elements=%d, n_collisions=%d\n"
66                 "\tmax_bucket_len=%d, n_used_buckets=%d, ave_bucket_len=%.2f\n"
67                 "\tn_hits=%d, n_misses=%d\n",
68                 hash->name,
69                 hash->n_buckets*(int)sizeof(hash_entry_t),
70                 hash->n_buckets,
71                 hash->n_elements,
72                 hash->n_collisions,
73                 hash->max_bucket_len,
74                 hash->n_used_buckets,
75                 (hash->n_used_buckets ?
76                         ((float)hash->n_elements)/hash->n_used_buckets : 0.0f),
77                 hash->n_hits,
78                 hash->n_misses);
79 }
80
81 void hash_table_deinit(hash_table_t *hash)
82 {
83     int i;
84     if (!hash)
85         return;
86
87     /* free the reminaing entries */
88     for (i = 0; i < hash->n_buckets; i++) {
89         hash_entry_t *hash_entry = (hash->entries + i);
90         free (hash_entry->key);
91         /* skip the first entry as this is part of the array */
92         hash_entry = hash_entry->next;
93         while (hash_entry)
94         {
95           hash_entry_t *old = hash_entry;
96           hash_entry = hash_entry->next;
97           free (old->key);
98           free (old);
99         }
100     }
101
102     free (hash->entries);
103
104     hash->entries = NULL;
105     hash->n_buckets = 0;
106 }
107
108 void *hash_table_get(hash_table_t *hash, const char *key)
109 {
110   int ndx= hash_index(hash, key);
111   hash_entry_t *hash_entry = hash->entries + ndx;
112   while (hash_entry) 
113   {
114     if (hash_entry->key) 
115     {
116       if (strcmp(key, hash_entry->key) == 0) {
117          // opkg_message(NULL, OPKG_DEBUG, "Function: %s. Key found for '%s' \n", __FUNCTION__, key);
118          hash->n_hits++;
119          return hash_entry->data;
120       }
121     }
122     hash_entry = hash_entry->next;
123   }
124   hash->n_misses++;
125   return NULL;
126 }
127
128 int hash_table_insert(hash_table_t *hash, const char *key, void *value)
129 {
130      int bucket_len = 0;
131      int ndx= hash_index(hash, key);
132      hash_entry_t *hash_entry = hash->entries + ndx;
133      if (hash_entry->key) {
134           if (strcmp(hash_entry->key, key) == 0) {
135                /* alread in table, update the value */
136                hash_entry->data = value;
137                return 0;
138           } else {
139                /* 
140                 * if this is a collision, we have to go to the end of the ll,
141                 * then add a new entry
142                 * before we can hook up the value
143                 */
144                while (hash_entry->next) {
145                     hash_entry = hash_entry->next;
146                     if (strcmp(hash_entry->key, key) == 0) {
147                         hash_entry->data = value;
148                         return 0;
149                     }
150                     bucket_len++;
151                }
152                hash_entry->next = xcalloc(1, sizeof(hash_entry_t));
153                hash_entry = hash_entry->next;
154                hash_entry->next = NULL;
155
156                hash->n_collisions++;
157                if (++bucket_len > hash->max_bucket_len)
158                     hash->max_bucket_len = bucket_len;
159           }
160      } else
161           hash->n_used_buckets++;
162
163      hash->n_elements++;
164      hash_entry->key = xstrdup(key);
165      hash_entry->data = value;
166
167      return 0;
168 }
169
170 int hash_table_remove(hash_table_t *hash, const char *key)
171 {
172     int ndx= hash_index(hash, key);
173     hash_entry_t *hash_entry = hash->entries + ndx;
174     hash_entry_t *next_entry=NULL, *last_entry=NULL;
175     while (hash_entry) 
176     {
177         if (hash_entry->key) 
178         {
179             if (strcmp(key, hash_entry->key) == 0) {
180                 free(hash_entry->key);
181                 if (last_entry) {
182                     last_entry->next = hash_entry->next;
183                     free(hash_entry);
184                 } else {
185                     next_entry = hash_entry->next;
186                     if (next_entry) {
187                         memmove(hash_entry, next_entry, sizeof(hash_entry_t));
188                         free(next_entry);
189                     } else {
190                         memset(hash_entry, 0 , sizeof(hash_entry_t));
191                     }
192                 }
193                 return 1;
194             }
195         }
196         last_entry = hash_entry;
197         hash_entry = hash_entry->next;
198     }
199     return 0;
200 }
201
202 void hash_table_foreach(hash_table_t *hash, void (*f)(const char *key, void *entry, void *data), void *data)
203
204     int i;
205     if (!hash || !f)
206         return;
207
208     for (i = 0; i < hash->n_buckets; i++) {
209         hash_entry_t *hash_entry = (hash->entries + i);
210         do {
211             if(hash_entry->key) {
212                 f(hash_entry->key, hash_entry->data, data);
213             }
214         } while((hash_entry = hash_entry->next));
215     }
216 }
217