hash.c 15.0 KB
Newer Older
D
Daniel Veillard 已提交
1
/*
2
 * hash.c: chained hash tables for domain and domain/connection deallocations
D
Daniel Veillard 已提交
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
 *
 * Reference: Your favorite introductory book on algorithms
 *
 * Copyright (C) 2000 Bjorn Reese and Daniel Veillard.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND
 * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER.
 *
 * Author: breese@users.sourceforge.net
18
 *         Daniel Veillard <veillard@redhat.com>
D
Daniel Veillard 已提交
19 20
 */

21
#include <config.h>
J
Jim Meyering 已提交
22

D
Daniel Veillard 已提交
23
#include <string.h>
24
#include <stdlib.h>
25 26

#include "virterror_internal.h"
D
Daniel Veillard 已提交
27
#include "hash.h"
28
#include "memory.h"
D
Daniel Veillard 已提交
29 30 31 32 33 34 35 36

#define MAX_HASH_LEN 8

/* #define DEBUG_GROW */

/*
 * A single entry in the hash table
 */
37 38 39 40
typedef struct _virHashEntry virHashEntry;
typedef virHashEntry *virHashEntryPtr;
struct _virHashEntry {
    struct _virHashEntry *next;
D
Daniel Veillard 已提交
41 42 43 44 45 46 47 48
    char *name;
    void *payload;
    int valid;
};

/*
 * The entire hash table
 */
49 50
struct _virHashTable {
    struct _virHashEntry *table;
D
Daniel Veillard 已提交
51 52 53 54 55
    int size;
    int nbElems;
};

/*
56
 * virHashComputeKey:
D
Daniel Veillard 已提交
57 58 59
 * Calculate the hash key
 */
static unsigned long
60 61
virHashComputeKey(virHashTablePtr table, const char *name)
{
D
Daniel Veillard 已提交
62 63
    unsigned long value = 0L;
    char ch;
64

D
Daniel Veillard 已提交
65
    if (name != NULL) {
66 67 68 69 70
        value += 30 * (*name);
        while ((ch = *name++) != 0) {
            value =
                value ^ ((value << 5) + (value >> 3) + (unsigned long) ch);
        }
D
Daniel Veillard 已提交
71 72 73 74 75
    }
    return (value % table->size);
}

/**
76
 * virHashCreate:
D
Daniel Veillard 已提交
77 78
 * @size: the size of the hash table
 *
79
 * Create a new virHashTablePtr.
D
Daniel Veillard 已提交
80 81 82
 *
 * Returns the newly created object, or NULL if an error occured.
 */
83
virHashTablePtr
84 85
virHashCreate(int size)
{
86
    virHashTablePtr table = NULL;
87

D
Daniel Veillard 已提交
88 89
    if (size <= 0)
        size = 256;
90

91 92 93 94 95 96 97 98
    if (VIR_ALLOC(table) < 0)
        return NULL;

    table->size = size;
    table->nbElems = 0;
    if (VIR_ALLOC_N(table->table, size) < 0) {
        VIR_FREE(table);
        return NULL;
D
Daniel Veillard 已提交
99
    }
100 101

    return table;
D
Daniel Veillard 已提交
102 103 104
}

/**
105
 * virHashGrow:
D
Daniel Veillard 已提交
106 107 108 109 110 111 112 113
 * @table: the hash table
 * @size: the new size of the hash table
 *
 * resize the hash table
 *
 * Returns 0 in case of success, -1 in case of failure
 */
static int
114 115
virHashGrow(virHashTablePtr table, int size)
{
D
Daniel Veillard 已提交
116 117
    unsigned long key;
    int oldsize, i;
118 119
    virHashEntryPtr iter, next;
    struct _virHashEntry *oldtable;
120

D
Daniel Veillard 已提交
121 122 123
#ifdef DEBUG_GROW
    unsigned long nbElem = 0;
#endif
124

D
Daniel Veillard 已提交
125
    if (table == NULL)
126
        return (-1);
D
Daniel Veillard 已提交
127
    if (size < 8)
128
        return (-1);
D
Daniel Veillard 已提交
129
    if (size > 8 * 2048)
130
        return (-1);
D
Daniel Veillard 已提交
131 132 133 134

    oldsize = table->size;
    oldtable = table->table;
    if (oldtable == NULL)
135 136
        return (-1);

137
    if (VIR_ALLOC_N(table->table, size) < 0) {
138 139
        table->table = oldtable;
        return (-1);
D
Daniel Veillard 已提交
140 141 142
    }
    table->size = size;

143
    /* If the two loops are merged, there would be situations where
144
     * a new entry needs to allocated and data copied into it from
145 146 147 148
     * the main table. So instead, we run through the array twice, first
     * copying all the elements in the main array (where we can't get
     * conflicts) and then the rest, so we only free (and don't allocate)
     */
D
Daniel Veillard 已提交
149
    for (i = 0; i < oldsize; i++) {
150 151 152 153 154
        if (oldtable[i].valid == 0)
            continue;
        key = virHashComputeKey(table, oldtable[i].name);
        memcpy(&(table->table[key]), &(oldtable[i]), sizeof(virHashEntry));
        table->table[key].next = NULL;
D
Daniel Veillard 已提交
155 156 157
    }

    for (i = 0; i < oldsize; i++) {
158 159 160 161 162 163 164 165 166 167 168 169
        iter = oldtable[i].next;
        while (iter) {
            next = iter->next;

            /*
             * put back the entry in the new table
             */

            key = virHashComputeKey(table, iter->name);
            if (table->table[key].valid == 0) {
                memcpy(&(table->table[key]), iter, sizeof(virHashEntry));
                table->table[key].next = NULL;
170
                VIR_FREE(iter);
171 172 173 174
            } else {
                iter->next = table->table[key].next;
                table->table[key].next = iter;
            }
D
Daniel Veillard 已提交
175 176

#ifdef DEBUG_GROW
177
            nbElem++;
D
Daniel Veillard 已提交
178 179
#endif

180 181
            iter = next;
        }
D
Daniel Veillard 已提交
182 183
    }

184
    VIR_FREE(oldtable);
D
Daniel Veillard 已提交
185 186 187

#ifdef DEBUG_GROW
    xmlGenericError(xmlGenericErrorContext,
188 189
                    "virHashGrow : from %d to %d, %d elems\n", oldsize,
                    size, nbElem);
D
Daniel Veillard 已提交
190 191
#endif

192
    return (0);
D
Daniel Veillard 已提交
193 194 195
}

/**
196
 * virHashFree:
D
Daniel Veillard 已提交
197 198 199 200 201 202 203
 * @table: the hash table
 * @f:  the deallocator function for items in the hash
 *
 * Free the hash @table and its contents. The userdata is
 * deallocated with @f if provided.
 */
void
204 205
virHashFree(virHashTablePtr table, virHashDeallocator f)
{
D
Daniel Veillard 已提交
206
    int i;
207 208
    virHashEntryPtr iter;
    virHashEntryPtr next;
D
Daniel Veillard 已提交
209 210 211 212
    int inside_table = 0;
    int nbElems;

    if (table == NULL)
213
        return;
D
Daniel Veillard 已提交
214
    if (table->table) {
215 216 217 218 219 220 221 222 223 224
        nbElems = table->nbElems;
        for (i = 0; (i < table->size) && (nbElems > 0); i++) {
            iter = &(table->table[i]);
            if (iter->valid == 0)
                continue;
            inside_table = 1;
            while (iter) {
                next = iter->next;
                if ((f != NULL) && (iter->payload != NULL))
                    f(iter->payload, iter->name);
225
                VIR_FREE(iter->name);
226 227
                iter->payload = NULL;
                if (!inside_table)
228
                    VIR_FREE(iter);
229 230 231 232 233
                nbElems--;
                inside_table = 0;
                iter = next;
            }
        }
234
        VIR_FREE(table->table);
D
Daniel Veillard 已提交
235
    }
236
    VIR_FREE(table);
D
Daniel Veillard 已提交
237 238 239
}

/**
240
 * virHashAddEntry:
D
Daniel Veillard 已提交
241 242 243 244 245 246 247 248 249 250
 * @table: the hash table
 * @name: the name of the userdata
 * @userdata: a pointer to the userdata
 *
 * Add the @userdata to the hash @table. This can later be retrieved
 * by using @name. Duplicate entries generate errors.
 *
 * Returns 0 the addition succeeded and -1 in case of error.
 */
int
251 252
virHashAddEntry(virHashTablePtr table, const char *name, void *userdata)
{
D
Daniel Veillard 已提交
253
    unsigned long key, len = 0;
254 255
    virHashEntryPtr entry;
    virHashEntryPtr insert;
D
Daniel Veillard 已提交
256 257

    if ((table == NULL) || (name == NULL))
258
        return (-1);
D
Daniel Veillard 已提交
259 260 261 262

    /*
     * Check for duplicate and insertion location.
     */
263
    key = virHashComputeKey(table, name);
D
Daniel Veillard 已提交
264
    if (table->table[key].valid == 0) {
265
        insert = NULL;
D
Daniel Veillard 已提交
266
    } else {
267 268
        for (insert = &(table->table[key]); insert->next != NULL;
             insert = insert->next) {
269
            if (STREQ(insert->name, name))
270 271 272
                return (-1);
            len++;
        }
273
        if (STREQ(insert->name, name))
274
            return (-1);
D
Daniel Veillard 已提交
275 276 277
    }

    if (insert == NULL) {
278
        entry = &(table->table[key]);
D
Daniel Veillard 已提交
279
    } else {
280
        if (VIR_ALLOC(entry) < 0)
281
            return (-1);
D
Daniel Veillard 已提交
282 283 284 285 286 287 288 289
    }

    entry->name = strdup(name);
    entry->payload = userdata;
    entry->next = NULL;
    entry->valid = 1;


290 291
    if (insert != NULL)
        insert->next = entry;
D
Daniel Veillard 已提交
292 293 294 295

    table->nbElems++;

    if (len > MAX_HASH_LEN)
296
        virHashGrow(table, MAX_HASH_LEN * table->size);
D
Daniel Veillard 已提交
297

298
    return (0);
D
Daniel Veillard 已提交
299 300 301
}

/**
302
 * virHashUpdateEntry:
D
Daniel Veillard 已提交
303 304 305 306 307 308 309 310 311 312 313 314
 * @table: the hash table
 * @name: the name of the userdata
 * @userdata: a pointer to the userdata
 * @f: the deallocator function for replaced item (if any)
 *
 * Add the @userdata to the hash @table. This can later be retrieved
 * by using @name. Existing entry for this tuple
 * will be removed and freed with @f if found.
 *
 * Returns 0 the addition succeeded and -1 in case of error.
 */
int
315
virHashUpdateEntry(virHashTablePtr table, const char *name,
316 317
                   void *userdata, virHashDeallocator f)
{
D
Daniel Veillard 已提交
318
    unsigned long key;
319 320
    virHashEntryPtr entry;
    virHashEntryPtr insert;
D
Daniel Veillard 已提交
321 322

    if ((table == NULL) || name == NULL)
323
        return (-1);
D
Daniel Veillard 已提交
324 325 326 327

    /*
     * Check for duplicate and insertion location.
     */
328
    key = virHashComputeKey(table, name);
D
Daniel Veillard 已提交
329
    if (table->table[key].valid == 0) {
330
        insert = NULL;
D
Daniel Veillard 已提交
331
    } else {
332 333
        for (insert = &(table->table[key]); insert->next != NULL;
             insert = insert->next) {
334
            if (STREQ(insert->name, name)) {
335 336 337 338 339 340
                if (f)
                    f(insert->payload, insert->name);
                insert->payload = userdata;
                return (0);
            }
        }
341
        if (STREQ(insert->name, name)) {
342 343 344 345 346
            if (f)
                f(insert->payload, insert->name);
            insert->payload = userdata;
            return (0);
        }
D
Daniel Veillard 已提交
347 348 349
    }

    if (insert == NULL) {
350
        entry = &(table->table[key]);
D
Daniel Veillard 已提交
351
    } else {
352
        if (VIR_ALLOC(entry) < 0)
353
            return (-1);
D
Daniel Veillard 已提交
354 355 356 357 358 359 360 361 362 363
    }

    entry->name = strdup(name);
    entry->payload = userdata;
    entry->next = NULL;
    entry->valid = 1;
    table->nbElems++;


    if (insert != NULL) {
364
        insert->next = entry;
D
Daniel Veillard 已提交
365
    }
366
    return (0);
D
Daniel Veillard 已提交
367 368 369
}

/**
370
 * virHashLookup:
D
Daniel Veillard 已提交
371 372 373 374 375 376 377 378
 * @table: the hash table
 * @name: the name of the userdata
 *
 * Find the userdata specified by the (@name, @name2, @name3) tuple.
 *
 * Returns the a pointer to the userdata
 */
void *
379 380
virHashLookup(virHashTablePtr table, const char *name)
{
D
Daniel Veillard 已提交
381
    unsigned long key;
382
    virHashEntryPtr entry;
D
Daniel Veillard 已提交
383 384

    if (table == NULL)
385
        return (NULL);
D
Daniel Veillard 已提交
386
    if (name == NULL)
387
        return (NULL);
388
    key = virHashComputeKey(table, name);
D
Daniel Veillard 已提交
389
    if (table->table[key].valid == 0)
390
        return (NULL);
D
Daniel Veillard 已提交
391
    for (entry = &(table->table[key]); entry != NULL; entry = entry->next) {
392
        if (STREQ(entry->name, name))
393
            return (entry->payload);
D
Daniel Veillard 已提交
394
    }
395
    return (NULL);
D
Daniel Veillard 已提交
396 397 398
}

/**
399
 * virHashSize:
D
Daniel Veillard 已提交
400 401 402 403 404 405 406 407
 * @table: the hash table
 *
 * Query the number of elements installed in the hash @table.
 *
 * Returns the number of elements in the hash table or
 * -1 in case of error
 */
int
408 409
virHashSize(virHashTablePtr table)
{
D
Daniel Veillard 已提交
410
    if (table == NULL)
411 412
        return (-1);
    return (table->nbElems);
D
Daniel Veillard 已提交
413 414 415
}

/**
416
 * virHashRemoveEntry:
D
Daniel Veillard 已提交
417 418 419 420 421 422 423 424 425 426 427
 * @table: the hash table
 * @name: the name of the userdata
 * @f: the deallocator function for removed item (if any)
 *
 * Find the userdata specified by the @name and remove
 * it from the hash @table. Existing userdata for this tuple will be removed
 * and freed with @f.
 *
 * Returns 0 if the removal succeeded and -1 in case of error or not found.
 */
int
428
virHashRemoveEntry(virHashTablePtr table, const char *name,
429 430
                   virHashDeallocator f)
{
D
Daniel Veillard 已提交
431
    unsigned long key;
432 433
    virHashEntryPtr entry;
    virHashEntryPtr prev = NULL;
D
Daniel Veillard 已提交
434 435

    if (table == NULL || name == NULL)
436
        return (-1);
D
Daniel Veillard 已提交
437

438
    key = virHashComputeKey(table, name);
D
Daniel Veillard 已提交
439
    if (table->table[key].valid == 0) {
440
        return (-1);
D
Daniel Veillard 已提交
441
    } else {
442 443
        for (entry = &(table->table[key]); entry != NULL;
             entry = entry->next) {
444
            if (STREQ(entry->name, name)) {
D
Daniel Veillard 已提交
445 446 447
                if ((f != NULL) && (entry->payload != NULL))
                    f(entry->payload, entry->name);
                entry->payload = NULL;
448
                VIR_FREE(entry->name);
449
                if (prev) {
D
Daniel Veillard 已提交
450
                    prev->next = entry->next;
451
                    VIR_FREE(entry);
452 453 454 455 456 457 458
                } else {
                    if (entry->next == NULL) {
                        entry->valid = 0;
                    } else {
                        entry = entry->next;
                        memcpy(&(table->table[key]), entry,
                               sizeof(virHashEntry));
459
                        VIR_FREE(entry);
460 461
                    }
                }
D
Daniel Veillard 已提交
462
                table->nbElems--;
463
                return (0);
D
Daniel Veillard 已提交
464 465 466
            }
            prev = entry;
        }
467
        return (-1);
D
Daniel Veillard 已提交
468 469
    }
}
470

471 472 473 474 475 476 477 478 479 480 481 482 483 484

/**
 * virHashForEach
 * @table: the hash table to process
 * @iter: callback to process each element
 * @data: opaque data to pass to the iterator
 *
 * Iterates over every element in the hash table, invoking the
 * 'iter' callback. The callback must not call any other virHash*
 * functions, and in particular must not attempt to remove the
 * element.
 *
 * Returns number of items iterated over upon completion, -1 on failure
 */
485
int virHashForEach(virHashTablePtr table, virHashIterator iter, void *data) {
486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531
    int i, count = 0;

    if (table == NULL || iter == NULL)
        return (-1);

    for (i = 0 ; i < table->size ; i++) {
        virHashEntryPtr entry = table->table + i;
        while (entry) {
            if (entry->valid) {
                iter(entry->payload, entry->name, data);
                count++;
            }
            entry = entry->next;
        }
    }
    return (count);
}

/**
 * virHashRemoveSet
 * @table: the hash table to process
 * @iter: callback to identify elements for removal
 * @f: callback to free memory from element payload
 * @data: opaque data to pass to the iterator
 *
 * Iterates over all elements in the hash table, invoking the 'iter'
 * callback. If the callback returns a non-zero value, the element
 * will be removed from the hash table & its payload passed to the
 * callback 'f' for de-allocation.
 *
 * Returns number of items removed on success, -1 on failure
 */
int virHashRemoveSet(virHashTablePtr table, virHashSearcher iter, virHashDeallocator f, const void *data) {
    int i, count = 0;

    if (table == NULL || iter == NULL)
        return (-1);

    for (i = 0 ; i < table->size ; i++) {
        virHashEntryPtr prev = NULL;
        virHashEntryPtr entry = &(table->table[i]);

        while (entry && entry->valid) {
            if (iter(entry->payload, entry->name, data)) {
                count++;
                f(entry->payload, entry->name);
532
                VIR_FREE(entry->name);
D
Daniel Veillard 已提交
533
                table->nbElems--;
534 535
                if (prev) {
                    prev->next = entry->next;
536
                    VIR_FREE(entry);
D
Daniel Veillard 已提交
537
                    entry = prev;
538 539 540 541 542 543 544 545
                } else {
                    if (entry->next == NULL) {
                        entry->valid = 0;
                        entry->name = NULL;
                    } else {
                        entry = entry->next;
                        memcpy(&(table->table[i]), entry,
                               sizeof(virHashEntry));
546
                        VIR_FREE(entry);
D
Daniel Veillard 已提交
547 548
                        entry = &(table->table[i]);
                        continue;
549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589
                    }
                }
            }
            prev = entry;
            if (entry) {
                entry = entry->next;
            }
        }
    }
    return (count);
}

/**
 * virHashSearch:
 * @table: the hash table to search
 * @iter: an iterator to identify the desired element
 * @data: extra opaque information passed to the iter
 *
 * Iterates over the hash table calling the 'iter' callback
 * for each element. The first element for which the iter
 * returns non-zero will be returned by this function.
 * The elements are processed in a undefined order
 */
void *virHashSearch(virHashTablePtr table, virHashSearcher iter, const void *data) {
    int i;

    if (table == NULL || iter == NULL)
        return (NULL);

    for (i = 0 ; i < table->size ; i++) {
        virHashEntryPtr entry = table->table + i;
        while (entry) {
            if (entry->valid) {
                if (iter(entry->payload, entry->name, data))
                    return entry->payload;
            }
            entry = entry->next;
        }
    }
    return (NULL);
}