blob: 0f4f1048d5aecf358b819bd932b4cfe2af1eab9f [file] [log] [blame]
Daniel Veillard3fe87682000-10-23 08:10:05 +00001/*
2 * hash.c: chained hash tables
3 *
4 * Reference: Your favorite introductory book on algorithms
5 *
6 * Copyright (C) 2000 Bjorn Reese and Daniel Veillard.
7 *
8 * Permission to use, copy, modify, and distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice appear in all copies.
11 *
12 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
13 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
14 * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND
15 * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER.
16 *
17 * Author: bjorn.reese@systematic.dk
18 */
19
Daniel Veillardc2def842000-11-07 14:21:01 +000020#ifdef WIN32
21#include "win32config.h"
22#else
23#include "config.h"
24#endif
25
Daniel Veillard9e8bfae2000-11-06 16:43:11 +000026#include <string.h>
Daniel Veillard3fe87682000-10-23 08:10:05 +000027#include <libxml/hash.h>
28#include <libxml/xmlmemory.h>
29#include <libxml/parser.h>
30
31/*
Daniel Veillard126f2792000-10-24 17:10:12 +000032 * A single entry in the hash table
33 */
34typedef struct _xmlHashEntry xmlHashEntry;
35typedef xmlHashEntry *xmlHashEntryPtr;
36struct _xmlHashEntry {
37 struct _xmlHashEntry *next;
38 xmlChar *name;
39 xmlChar *name2;
40 xmlChar *name3;
41 void *payload;
42};
43
44/*
45 * The entire hash table
46 */
47struct _xmlHashTable {
48 struct _xmlHashEntry **table;
49 int size;
50};
51
52/*
Daniel Veillard3fe87682000-10-23 08:10:05 +000053 * xmlHashComputeKey:
54 * Calculate the hash key
55 */
56static unsigned long
57xmlHashComputeKey(xmlHashTablePtr table, const xmlChar *string) {
58 unsigned long value = 0L;
59 char ch;
60
61 while ((ch = *string++) != 0) {
62 /* value *= 31; */
63 value += (unsigned long)ch;
64 }
65 return (value % table->size);
66}
67
68/**
69 * xmlHashCreate:
70 * @size: the size of the hash table
71 *
72 * Create a new xmlHashTablePtr.
73 *
74 * Returns the newly created object, or NULL if an error occured.
75 */
76xmlHashTablePtr
77xmlHashCreate(int size) {
78 xmlHashTablePtr table;
79
80 if (size <= 0)
81 size = 256;
82
83 table = xmlMalloc(sizeof(xmlHashTable));
84 if (table) {
85 table->size = size;
86 table->table = xmlMalloc(size * sizeof(xmlHashEntry));
87 if (table->table) {
88 memset(table->table, 0, size * sizeof(xmlHashEntry));
89 return(table);
90 }
91 xmlFree(table);
92 }
93 return(NULL);
94}
95
96/**
97 * xmlHashFree:
98 * @table: the hash table
99 * @f: the deallocator function for items in the hash
100 *
101 * Free the hash table and its contents. The userdata is
102 * deallocated with f if provided.
103 */
104void
105xmlHashFree(xmlHashTablePtr table, xmlHashDeallocator f) {
106 int i;
107 xmlHashEntryPtr iter;
108 xmlHashEntryPtr next;
109
110 if (table == NULL)
111 return;
112 if (table->table) {
113 for(i = 0; i < table->size; i++) {
114 iter = table->table[i];
115 while (iter) {
116 next = iter->next;
117 if (iter->name)
118 xmlFree(iter->name);
Daniel Veillard126f2792000-10-24 17:10:12 +0000119 if (iter->name2)
120 xmlFree(iter->name2);
121 if (iter->name3)
122 xmlFree(iter->name3);
Daniel Veillard3fe87682000-10-23 08:10:05 +0000123 if (f)
Daniel Veillard126f2792000-10-24 17:10:12 +0000124 f(iter->payload, iter->name);
Daniel Veillard3fe87682000-10-23 08:10:05 +0000125 iter->payload = NULL;
126 xmlFree(iter);
127 iter = next;
128 }
129 table->table[i] = NULL;
130 }
131 xmlFree(table->table);
132 }
133 xmlFree(table);
134}
135
136/**
137 * xmlHashAddEntry:
138 * @table: the hash table
139 * @name: the name of the userdata
140 * @userdata: a pointer to the userdata
141 *
142 * Add the userdata to the hash table. This can later be retrieved
143 * by using the name. Duplicate names generate errors.
144 *
145 * Returns 0 the addition succeeded and -1 in case of error.
146 */
147int
148xmlHashAddEntry(xmlHashTablePtr table, const xmlChar *name, void *userdata) {
Daniel Veillard126f2792000-10-24 17:10:12 +0000149 return(xmlHashAddEntry3(table, name, NULL, NULL, userdata));
150}
Daniel Veillard3fe87682000-10-23 08:10:05 +0000151
Daniel Veillard126f2792000-10-24 17:10:12 +0000152/**
153 * xmlHashAddEntry2:
154 * @table: the hash table
155 * @name: the name of the userdata
156 * @name2: a second name of the userdata
157 * @userdata: a pointer to the userdata
158 *
159 * Add the userdata to the hash table. This can later be retrieved
160 * by using the (name, name2) tuple. Duplicate tuples generate errors.
161 *
162 * Returns 0 the addition succeeded and -1 in case of error.
163 */
164int
165xmlHashAddEntry2(xmlHashTablePtr table, const xmlChar *name,
166 const xmlChar *name2, void *userdata) {
167 return(xmlHashAddEntry3(table, name, name2, NULL, userdata));
Daniel Veillard3fe87682000-10-23 08:10:05 +0000168}
169
170/**
171 * xmlHashUpdateEntry:
172 * @table: the hash table
173 * @name: the name of the userdata
174 * @userdata: a pointer to the userdata
175 * @f: the deallocator function for replaced item (if any)
176 *
177 * Add the userdata to the hash table. This can later be retrieved
178 * by using the name. Existing entry for this name will be removed
179 * and freed with @f if found.
180 *
181 * Returns 0 the addition succeeded and -1 in case of error.
182 */
183int
184xmlHashUpdateEntry(xmlHashTablePtr table, const xmlChar *name,
185 void *userdata, xmlHashDeallocator f) {
Daniel Veillard126f2792000-10-24 17:10:12 +0000186 return(xmlHashUpdateEntry3(table, name, NULL, NULL, userdata, f));
187}
188
189/**
190 * xmlHashUpdateEntry2:
191 * @table: the hash table
192 * @name: the name of the userdata
193 * @name2: a second name of the userdata
194 * @userdata: a pointer to the userdata
195 * @f: the deallocator function for replaced item (if any)
196 *
197 * Add the userdata to the hash table. This can later be retrieved
198 * by using the (name, name2) tuple. Existing entry for this tuple will
199 * be removed and freed with @f if found.
200 *
201 * Returns 0 the addition succeeded and -1 in case of error.
202 */
203int
204xmlHashUpdateEntry2(xmlHashTablePtr table, const xmlChar *name,
205 const xmlChar *name2, void *userdata,
206 xmlHashDeallocator f) {
207 return(xmlHashUpdateEntry3(table, name, name2, NULL, userdata, f));
208}
209
210/**
211 * xmlHashLookup:
212 * @table: the hash table
213 * @name: the name of the userdata
214 *
215 * Find the userdata specified by the name.
216 *
217 * Returns the a pointer to the userdata
218 */
219void *
220xmlHashLookup(xmlHashTablePtr table, const xmlChar *name) {
221 return(xmlHashLookup3(table, name, NULL, NULL));
222}
223
224/**
225 * xmlHashLookup2:
226 * @table: the hash table
227 * @name: the name of the userdata
228 * @name2: a second name of the userdata
229 *
230 * Find the userdata specified by the (name, name2) tuple.
231 *
232 * Returns the a pointer to the userdata
233 */
234void *
235xmlHashLookup2(xmlHashTablePtr table, const xmlChar *name,
236 const xmlChar *name2) {
237 return(xmlHashLookup3(table, name, name2, NULL));
238}
239
240/**
241 * xmlHashAddEntry3:
242 * @table: the hash table
243 * @name: the name of the userdata
244 * @name2: a second name of the userdata
245 * @name3: a third name of the userdata
246 * @userdata: a pointer to the userdata
247 *
248 * Add the userdata to the hash table. This can later be retrieved
249 * by using the tuple (name, name2, name3). Duplicate entries generate
250 * errors.
251 *
252 * Returns 0 the addition succeeded and -1 in case of error.
253 */
254int
255xmlHashAddEntry3(xmlHashTablePtr table, const xmlChar *name,
256 const xmlChar *name2, const xmlChar *name3,
257 void *userdata) {
Daniel Veillard3fe87682000-10-23 08:10:05 +0000258 unsigned long key;
259 xmlHashEntryPtr entry;
260 xmlHashEntryPtr insert;
261
262 if ((table == NULL) || name == NULL)
263 return(-1);
264
265 /*
266 * Check for duplicate and insertion location.
267 */
268 key = xmlHashComputeKey(table, name);
269 if (table->table[key] == NULL) {
270 insert = NULL;
271 } else {
272 for (insert = table->table[key]; insert->next != NULL;
273 insert = insert->next) {
Daniel Veillard126f2792000-10-24 17:10:12 +0000274 if ((xmlStrEqual(insert->name, name)) &&
275 (xmlStrEqual(insert->name2, name2)) &&
276 (xmlStrEqual(insert->name3, name3)))
277 return(-1);
278 }
279 if ((xmlStrEqual(insert->name, name)) &&
280 (xmlStrEqual(insert->name2, name2)) &&
281 (xmlStrEqual(insert->name3, name3)))
282 return(-1);
283 }
284
285 entry = xmlMalloc(sizeof(xmlHashEntry));
286 if (entry == NULL)
287 return(-1);
288 entry->name = xmlStrdup(name);
289 entry->name2 = xmlStrdup(name2);
290 entry->name3 = xmlStrdup(name3);
291 entry->payload = userdata;
292 entry->next = NULL;
293
294
295 if (insert == NULL) {
296 table->table[key] = entry;
297 } else {
298 insert->next = entry;
299 }
300 return(0);
301}
302
303/**
304 * xmlHashUpdateEntry3:
305 * @table: the hash table
306 * @name: the name of the userdata
307 * @name2: a second name of the userdata
308 * @name3: a third name of the userdata
309 * @userdata: a pointer to the userdata
310 * @f: the deallocator function for replaced item (if any)
311 *
312 * Add the userdata to the hash table. This can later be retrieved
313 * by using the tuple (name, name2, name3). Existing entry for this tuple
314 * will be removed and freed with @f if found.
315 *
316 * Returns 0 the addition succeeded and -1 in case of error.
317 */
318int
319xmlHashUpdateEntry3(xmlHashTablePtr table, const xmlChar *name,
320 const xmlChar *name2, const xmlChar *name3,
321 void *userdata, xmlHashDeallocator f) {
322 unsigned long key;
323 xmlHashEntryPtr entry;
324 xmlHashEntryPtr insert;
325
326 if ((table == NULL) || name == NULL)
327 return(-1);
328
329 /*
330 * Check for duplicate and insertion location.
331 */
332 key = xmlHashComputeKey(table, name);
333 if (table->table[key] == NULL) {
334 insert = NULL;
335 } else {
336 for (insert = table->table[key]; insert->next != NULL;
337 insert = insert->next) {
338 if ((xmlStrEqual(insert->name, name)) &&
339 (xmlStrEqual(insert->name2, name2)) &&
340 (xmlStrEqual(insert->name3, name3))) {
Daniel Veillard3fe87682000-10-23 08:10:05 +0000341 if (f)
Daniel Veillard126f2792000-10-24 17:10:12 +0000342 f(insert->payload, insert->name);
Daniel Veillard3fe87682000-10-23 08:10:05 +0000343 insert->payload = userdata;
344 return(0);
345 }
346 }
Daniel Veillard126f2792000-10-24 17:10:12 +0000347 if ((xmlStrEqual(insert->name, name)) &&
348 (xmlStrEqual(insert->name2, name2)) &&
349 (xmlStrEqual(insert->name3, name3))) {
Daniel Veillard3fe87682000-10-23 08:10:05 +0000350 if (f)
Daniel Veillard126f2792000-10-24 17:10:12 +0000351 f(insert->payload, insert->name);
Daniel Veillard3fe87682000-10-23 08:10:05 +0000352 insert->payload = userdata;
353 return(0);
354 }
355 }
356
357 entry = xmlMalloc(sizeof(xmlHashEntry));
358 if (entry == NULL)
359 return(-1);
360 entry->name = xmlStrdup(name);
Daniel Veillard126f2792000-10-24 17:10:12 +0000361 entry->name2 = xmlStrdup(name2);
362 entry->name3 = xmlStrdup(name3);
Daniel Veillard3fe87682000-10-23 08:10:05 +0000363 entry->payload = userdata;
364 entry->next = NULL;
365
366
367 if (insert == NULL) {
368 table->table[key] = entry;
369 } else {
370 insert->next = entry;
371 }
372 return(0);
373}
374
375/**
376 * xmlHashLookup:
377 * @table: the hash table
378 * @name: the name of the userdata
Daniel Veillard126f2792000-10-24 17:10:12 +0000379 * @name2: a second name of the userdata
380 * @name3: a third name of the userdata
Daniel Veillard3fe87682000-10-23 08:10:05 +0000381 *
Daniel Veillard126f2792000-10-24 17:10:12 +0000382 * Find the userdata specified by the (name, name2, name3) tuple.
Daniel Veillard3fe87682000-10-23 08:10:05 +0000383 *
384 * Returns the a pointer to the userdata
385 */
386void *
Daniel Veillard126f2792000-10-24 17:10:12 +0000387xmlHashLookup3(xmlHashTablePtr table, const xmlChar *name,
388 const xmlChar *name2, const xmlChar *name3) {
Daniel Veillard3fe87682000-10-23 08:10:05 +0000389 unsigned long key;
390 xmlHashEntryPtr entry;
391
392 if (table == NULL)
393 return(NULL);
394 if (name == NULL)
395 return(NULL);
396 key = xmlHashComputeKey(table, name);
397 for (entry = table->table[key]; entry != NULL; entry = entry->next) {
Daniel Veillard126f2792000-10-24 17:10:12 +0000398 if ((xmlStrEqual(entry->name, name)) &&
399 (xmlStrEqual(entry->name2, name2)) &&
400 (xmlStrEqual(entry->name3, name3)))
Daniel Veillard3fe87682000-10-23 08:10:05 +0000401 return(entry->payload);
402 }
403 return(NULL);
404}
405
406/**
407 * xmlHashScan:
408 * @table: the hash table
409 * @f: the scanner function for items in the hash
410 * @data: extra data passed to f
411 *
412 * Scan the hash table and applied f to each value.
413 */
414void
415xmlHashScan(xmlHashTablePtr table, xmlHashScanner f, void *data) {
416 int i;
417 xmlHashEntryPtr iter;
418 xmlHashEntryPtr next;
419
420 if (table == NULL)
421 return;
422 if (f == NULL)
423 return;
424
425 if (table->table) {
426 for(i = 0; i < table->size; i++) {
427 iter = table->table[i];
428 while (iter) {
429 next = iter->next;
430 if (f)
Daniel Veillard126f2792000-10-24 17:10:12 +0000431 f(iter->payload, data, iter->name);
432 iter = next;
433 }
434 }
435 }
436}
437
438/**
439 * xmlHashScan3:
440 * @table: the hash table
441 * @name: the name of the userdata or NULL
442 * @name2: a second name of the userdata or NULL
443 * @name3: a third name of the userdata or NULL
444 * @f: the scanner function for items in the hash
445 * @data: extra data passed to f
446 *
447 * Scan the hash table and applied f to each value matching
448 * (name, name2, name3) tuple. If one of the names is null,
449 * the comparison is considered to match.
450 */
451void
452xmlHashScan3(xmlHashTablePtr table, const xmlChar *name,
453 const xmlChar *name2, const xmlChar *name3,
454 xmlHashScanner f, void *data) {
455 int i;
456 xmlHashEntryPtr iter;
457 xmlHashEntryPtr next;
458
459 if (table == NULL)
460 return;
461 if (f == NULL)
462 return;
463
464 if (table->table) {
465 for(i = 0; i < table->size; i++) {
466 iter = table->table[i];
467 while (iter) {
468 next = iter->next;
469 if (((name == NULL) || (xmlStrEqual(name, iter->name))) &&
470 ((name2 == NULL) || (xmlStrEqual(name2, iter->name2))) &&
471 ((name3 == NULL) || (xmlStrEqual(name3, iter->name3)))) {
472 f(iter->payload, data, iter->name);
473 }
Daniel Veillard3fe87682000-10-23 08:10:05 +0000474 iter = next;
475 }
476 }
477 }
478}
479
480/**
481 * xmlHashCopy:
482 * @table: the hash table
483 * @f: the copier function for items in the hash
484 *
485 * Scan the hash table and applied f to each value.
486 *
487 * Returns the new table or NULL in case of error.
488 */
489xmlHashTablePtr
490xmlHashCopy(xmlHashTablePtr table, xmlHashCopier f) {
491 int i;
492 xmlHashEntryPtr iter;
493 xmlHashEntryPtr next;
494 xmlHashTablePtr ret;
495
496 if (table == NULL)
497 return(NULL);
498 if (f == NULL)
499 return(NULL);
500
501 ret = xmlHashCreate(table->size);
502 if (table->table) {
503 for(i = 0; i < table->size; i++) {
504 iter = table->table[i];
505 while (iter) {
506 next = iter->next;
Daniel Veillard126f2792000-10-24 17:10:12 +0000507 xmlHashAddEntry3(ret, iter->name, iter->name2,
508 iter->name3, f(iter->payload, iter->name));
Daniel Veillard3fe87682000-10-23 08:10:05 +0000509 iter = next;
510 }
511 }
512 }
513 return(ret);
514}
515