blob: f7969b7673edbacbc1ae3ea94e4ba964acd5ca51 [file] [log] [blame]
njn3e884182003-04-15 13:03:23 +00001
2/*--------------------------------------------------------------------*/
njnf69f9452005-07-03 17:53:11 +00003/*--- A separately-chained hash table. m_hashtable.c ---*/
njn3e884182003-04-15 13:03:23 +00004/*--------------------------------------------------------------------*/
5
6/*
njnb9c427c2004-12-01 14:14:42 +00007 This file is part of Valgrind, a dynamic binary instrumentation
8 framework.
njn3e884182003-04-15 13:03:23 +00009
sewardj9ebd6e02007-01-08 06:01:59 +000010 Copyright (C) 2005-2007 Nicholas Nethercote
sewardj0c24f8a2006-10-17 02:11:55 +000011 njn@valgrind.org
njn3e884182003-04-15 13:03:23 +000012
13 This program is free software; you can redistribute it and/or
14 modify it under the terms of the GNU General Public License as
15 published by the Free Software Foundation; either version 2 of the
16 License, or (at your option) any later version.
17
18 This program is distributed in the hope that it will be useful, but
19 WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 General Public License for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software
25 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
26 02111-1307, USA.
27
28 The GNU General Public License is contained in the file COPYING.
29*/
30
njnc7561b92005-06-19 01:24:32 +000031#include "pub_core_basics.h"
sewardj3f94a7d2007-08-25 07:19:08 +000032#include "pub_core_debuglog.h"
njn81c00df2005-05-14 21:28:43 +000033#include "pub_core_hashtable.h"
njn132bfcc2005-06-04 19:16:06 +000034#include "pub_core_libcassert.h"
njnaf1d7df2005-06-11 01:31:52 +000035#include "pub_core_mallocfree.h"
njn3e884182003-04-15 13:03:23 +000036
37/*--------------------------------------------------------------------*/
38/*--- Declarations ---*/
39/*--------------------------------------------------------------------*/
40
njnb8824022005-07-03 17:10:04 +000041#define CHAIN_NO(key,tbl) (((UWord)(key)) % tbl->n_chains)
42
njnf69f9452005-07-03 17:53:11 +000043struct _VgHashTable {
sewardj3f94a7d2007-08-25 07:19:08 +000044 UInt n_chains; // should be prime
45 UInt n_elements;
46 VgHashNode* iterNode; // current iterator node
47 UInt iterChain; // next chain to be traversed by the iterator
48 VgHashNode** chains; // expanding array of hash chains
49 Bool iterOK; // table safe to iterate over?
50 HChar* name; // name of table (for debugging only)
51};
52
53#define N_HASH_PRIMES 20
54
55static SizeT primes[N_HASH_PRIMES] = {
56 769UL, 1543UL, 3079UL, 6151UL,
57 12289UL, 24593UL, 49157UL, 98317UL,
58 196613UL, 393241UL, 786433UL, 1572869UL,
59 3145739UL, 6291469UL, 12582917UL, 25165843UL,
60 50331653UL, 100663319UL, 201326611UL, 402653189UL
njnf69f9452005-07-03 17:53:11 +000061};
njn3e884182003-04-15 13:03:23 +000062
63/*--------------------------------------------------------------------*/
64/*--- Functions ---*/
65/*--------------------------------------------------------------------*/
66
sewardj3f94a7d2007-08-25 07:19:08 +000067VgHashTable VG_(HT_construct) ( HChar* name )
njn3e884182003-04-15 13:03:23 +000068{
njnce46c2a2003-07-21 10:52:07 +000069 /* Initialises to zero, ie. all entries NULL */
sewardj3f94a7d2007-08-25 07:19:08 +000070 SizeT n_chains = primes[0];
71 SizeT sz = n_chains * sizeof(VgHashNode*);
72 VgHashTable table = VG_(calloc)(1, sizeof(struct _VgHashTable));
73 table->chains = VG_(calloc)(1, sz);
74 table->n_chains = n_chains;
75 table->n_elements = 0;
76 table->iterOK = True;
77 table->name = name;
78 vg_assert(name);
njnf69f9452005-07-03 17:53:11 +000079 return table;
njn3e884182003-04-15 13:03:23 +000080}
81
82Int VG_(HT_count_nodes) ( VgHashTable table )
83{
sewardj3f94a7d2007-08-25 07:19:08 +000084 return table->n_elements;
85}
njn3e884182003-04-15 13:03:23 +000086
sewardj3f94a7d2007-08-25 07:19:08 +000087static void resize ( VgHashTable table )
88{
89 Int i;
90 SizeT sz;
91 SizeT old_chains = table->n_chains;
92 SizeT new_chains = old_chains + 1;
93 VgHashNode** chains;
94 VgHashNode * node;
95
96 /* If we've run out of primes, do nothing. */
97 if (old_chains == primes[N_HASH_PRIMES-1])
98 return;
99
100 vg_assert(old_chains >= primes[0]
101 && old_chains < primes[N_HASH_PRIMES-1]);
102
103 for (i = 0; i < N_HASH_PRIMES; i++) {
104 if (primes[i] > new_chains) {
105 new_chains = primes[i];
106 break;
107 }
108 }
109
110 vg_assert(new_chains > old_chains);
111 vg_assert(new_chains > primes[0]
112 && new_chains <= primes[N_HASH_PRIMES-1]);
113
114 VG_(debugLog)(
115 1, "hashtable",
116 "resizing table `%s' from %lu to %lu (total elems %lu)\n",
117 table->name, (UWord)old_chains, (UWord)new_chains,
118 (UWord)table->n_elements );
119
120 table->n_chains = new_chains;
121 sz = new_chains * sizeof(VgHashNode*);
122 chains = VG_(calloc)(1, sz);
123
124 for (i = 0; i < old_chains; i++) {
125 node = table->chains[i];
126 while (node != NULL) {
127 VgHashNode* next = node->next;
128 UWord chain = CHAIN_NO(node->key, table);
129 node->next = chains[chain];
130 chains[chain] = node;
131 node = next;
132 }
133 }
134
135 VG_(free)(table->chains);
136 table->chains = chains;
njn3e884182003-04-15 13:03:23 +0000137}
138
njnf69f9452005-07-03 17:53:11 +0000139/* Puts a new, heap allocated VgHashNode, into the VgHashTable. Prepends
140 the node to the appropriate chain. */
njn246a9d22005-08-14 06:24:20 +0000141void VG_(HT_add_node) ( VgHashTable table, void* vnode )
njn3e884182003-04-15 13:03:23 +0000142{
njn246a9d22005-08-14 06:24:20 +0000143 VgHashNode* node = (VgHashNode*)vnode;
sewardj3f94a7d2007-08-25 07:19:08 +0000144 UWord chain = CHAIN_NO(node->key, table);
njnf69f9452005-07-03 17:53:11 +0000145 node->next = table->chains[chain];
146 table->chains[chain] = node;
sewardj3f94a7d2007-08-25 07:19:08 +0000147 table->n_elements++;
148 if ( (1 * (ULong)table->n_elements) > (1 * (ULong)table->n_chains) ) {
149 resize(table);
njn3e884182003-04-15 13:03:23 +0000150 }
151
sewardj3f94a7d2007-08-25 07:19:08 +0000152 /* Table has been modified; hence HT_Next should assert. */
153 table->iterOK = False;
njn3e884182003-04-15 13:03:23 +0000154}
155
njn34582fb2005-08-11 00:06:36 +0000156/* Looks up a VgHashNode in the table. Returns NULL if not found. */
njn246a9d22005-08-14 06:24:20 +0000157void* VG_(HT_lookup) ( VgHashTable table, UWord key )
njn34582fb2005-08-11 00:06:36 +0000158{
159 VgHashNode* curr = table->chains[ CHAIN_NO(key, table) ];
160
161 while (curr) {
162 if (key == curr->key) {
163 return curr;
164 }
165 curr = curr->next;
166 }
167 return NULL;
168}
169
170/* Removes a VgHashNode from the table. Returns NULL if not found. */
njn246a9d22005-08-14 06:24:20 +0000171void* VG_(HT_remove) ( VgHashTable table, UWord key )
njn34582fb2005-08-11 00:06:36 +0000172{
sewardj3f94a7d2007-08-25 07:19:08 +0000173 UWord chain = CHAIN_NO(key, table);
njn34582fb2005-08-11 00:06:36 +0000174 VgHashNode* curr = table->chains[chain];
175 VgHashNode** prev_next_ptr = &(table->chains[chain]);
176
sewardj3f94a7d2007-08-25 07:19:08 +0000177 /* Table has been modified; hence HT_Next should assert. */
178 table->iterOK = False;
179
njn34582fb2005-08-11 00:06:36 +0000180 while (curr) {
181 if (key == curr->key) {
182 *prev_next_ptr = curr->next;
sewardj3f94a7d2007-08-25 07:19:08 +0000183 table->n_elements--;
njn34582fb2005-08-11 00:06:36 +0000184 return curr;
185 }
186 prev_next_ptr = &(curr->next);
187 curr = curr->next;
188 }
189 return NULL;
190}
191
sewardj3f94a7d2007-08-25 07:19:08 +0000192/* Allocates a suitably-sized array, copies all the hashtable elements
193 into it, then returns both the array and the size of it. This is
194 used by the memory-leak detector. The array must be freed with
195 VG_(free).
njn3e884182003-04-15 13:03:23 +0000196*/
sewardj3f94a7d2007-08-25 07:19:08 +0000197VgHashNode** VG_(HT_to_array) ( VgHashTable table, /*OUT*/ UInt* n_elems )
njn3e884182003-04-15 13:03:23 +0000198{
199 UInt i, j;
200 VgHashNode** arr;
201 VgHashNode* node;
202
sewardj3f94a7d2007-08-25 07:19:08 +0000203 *n_elems = 0;
njnb8824022005-07-03 17:10:04 +0000204 for (i = 0; i < table->n_chains; i++) {
205 for (node = table->chains[i]; node != NULL; node = node->next) {
sewardj3f94a7d2007-08-25 07:19:08 +0000206 (*n_elems)++;
njn3e884182003-04-15 13:03:23 +0000207 }
208 }
sewardj3f94a7d2007-08-25 07:19:08 +0000209 if (*n_elems == 0)
njn3e884182003-04-15 13:03:23 +0000210 return NULL;
211
sewardj3f94a7d2007-08-25 07:19:08 +0000212 arr = VG_(malloc)( *n_elems * sizeof(VgHashNode*) );
njn3e884182003-04-15 13:03:23 +0000213
214 j = 0;
njnb8824022005-07-03 17:10:04 +0000215 for (i = 0; i < table->n_chains; i++) {
216 for (node = table->chains[i]; node != NULL; node = node->next) {
njn3e884182003-04-15 13:03:23 +0000217 arr[j++] = node;
218 }
219 }
sewardj3f94a7d2007-08-25 07:19:08 +0000220 vg_assert(j == *n_elems);
njn3e884182003-04-15 13:03:23 +0000221
njn3e884182003-04-15 13:03:23 +0000222 return arr;
223}
224
njn1af79722005-08-14 17:42:35 +0000225void VG_(HT_ResetIter)(VgHashTable table)
226{
227 vg_assert(table);
228 table->iterNode = NULL;
229 table->iterChain = 0;
sewardj3f94a7d2007-08-25 07:19:08 +0000230 table->iterOK = True;
njn1af79722005-08-14 17:42:35 +0000231}
232
233void* VG_(HT_Next)(VgHashTable table)
234{
235 Int i;
236 vg_assert(table);
sewardj3f94a7d2007-08-25 07:19:08 +0000237 /* See long comment on HT_Next prototype in pub_tool_hashtable.h.
238 In short if this fails, it means the caller tried to modify the
239 table whilst iterating over it, which is a bug. */
240 vg_assert(table->iterOK);
241
njn1af79722005-08-14 17:42:35 +0000242 if (table->iterNode && table->iterNode->next) {
243 table->iterNode = table->iterNode->next;
244 return table->iterNode;
245 }
246
247 for (i = table->iterChain; i < table->n_chains; i++) {
248 if (table->chains[i]) {
249 table->iterNode = table->chains[i];
250 table->iterChain = i + 1; // Next chain to be traversed
251 return table->iterNode;
252 }
253 }
254 return NULL;
255}
256
njn3e884182003-04-15 13:03:23 +0000257void VG_(HT_destruct)(VgHashTable table)
258{
sewardj755e1262005-12-24 15:33:32 +0000259 UInt i;
260 VgHashNode *node, *node_next;
sewardj3f94a7d2007-08-25 07:19:08 +0000261
njnb8824022005-07-03 17:10:04 +0000262 for (i = 0; i < table->n_chains; i++) {
sewardj755e1262005-12-24 15:33:32 +0000263 for (node = table->chains[i]; node != NULL; node = node_next) {
264 node_next = node->next;
njn3e884182003-04-15 13:03:23 +0000265 VG_(free)(node);
266 }
267 }
sewardj3f94a7d2007-08-25 07:19:08 +0000268 VG_(free)(table->chains);
njn3e884182003-04-15 13:03:23 +0000269 VG_(free)(table);
270}
271
272/*--------------------------------------------------------------------*/
njn81c00df2005-05-14 21:28:43 +0000273/*--- end ---*/
njn3e884182003-04-15 13:03:23 +0000274/*--------------------------------------------------------------------*/