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