hash.c 17.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
 *
 * Reference: Your favorite introductory book on algorithms
 *
E
Eric Blake 已提交
6
 * Copyright (C) 2011 Red Hat, Inc.
D
Daniel Veillard 已提交
7 8 9 10 11 12 13 14 15 16 17 18
 * 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
19
 *         Daniel Veillard <veillard@redhat.com>
D
Daniel Veillard 已提交
20 21
 */

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

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

#include "virterror_internal.h"
D
Daniel Veillard 已提交
28
#include "hash.h"
29
#include "memory.h"
E
Eric Blake 已提交
30
#include "logging.h"
D
Daniel Veillard 已提交
31

32 33
#define VIR_FROM_THIS VIR_FROM_NONE

D
Daniel Veillard 已提交
34 35 36 37 38 39 40
#define MAX_HASH_LEN 8

/* #define DEBUG_GROW */

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

/*
 * The entire hash table
 */
53 54
struct _virHashTable {
    struct _virHashEntry *table;
D
Daniel Veillard 已提交
55 56
    int size;
    int nbElems;
57
    virHashDataFree dataFree;
58 59 60 61
    virHashKeyCode keyCode;
    virHashKeyEqual keyEqual;
    virHashKeyCopy keyCopy;
    virHashKeyFree keyFree;
D
Daniel Veillard 已提交
62 63
};

64
static unsigned long virHashStrCode(const void *name)
65
{
66
    const char *str = name;
D
Daniel Veillard 已提交
67 68
    unsigned long value = 0L;
    char ch;
69

70 71 72
    if (str != NULL) {
        value += 30 * (*str);
        while ((ch = *str++) != 0) {
73 74 75
            value =
                value ^ ((value << 5) + (value >> 3) + (unsigned long) ch);
        }
D
Daniel Veillard 已提交
76
    }
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
    return value;
}

static bool virHashStrEqual(const void *namea, const void *nameb)
{
    return STREQ(namea, nameb);
}

static void *virHashStrCopy(const void *name)
{
    return strdup(name);
}

static void virHashStrFree(void *name)
{
    VIR_FREE(name);
}


static unsigned long
virHashComputeKey(virHashTablePtr table, const void *name)
{
    unsigned long value = table->keyCode(name);
D
Daniel Veillard 已提交
100 101 102 103
    return (value % table->size);
}

/**
104
 * virHashCreateFull:
D
Daniel Veillard 已提交
105
 * @size: the size of the hash table
106 107 108 109 110
 * @dataFree: callback to free data
 * @keyCode: callback to compute hash code
 * @keyEqual: callback to compare hash keys
 * @keyCopy: callback to copy hash keys
 * @keyFree: callback to free keys
D
Daniel Veillard 已提交
111
 *
112
 * Create a new virHashTablePtr.
D
Daniel Veillard 已提交
113 114 115
 *
 * Returns the newly created object, or NULL if an error occured.
 */
116 117 118 119 120 121
virHashTablePtr virHashCreateFull(int size,
                                  virHashDataFree dataFree,
                                  virHashKeyCode keyCode,
                                  virHashKeyEqual keyEqual,
                                  virHashKeyCopy keyCopy,
                                  virHashKeyFree keyFree)
122
{
123
    virHashTablePtr table = NULL;
124

D
Daniel Veillard 已提交
125 126
    if (size <= 0)
        size = 256;
127

128 129
    if (VIR_ALLOC(table) < 0) {
        virReportOOMError();
130
        return NULL;
131
    }
132 133 134

    table->size = size;
    table->nbElems = 0;
135
    table->dataFree = dataFree;
136 137 138 139 140
    table->keyCode = keyCode;
    table->keyEqual = keyEqual;
    table->keyCopy = keyCopy;
    table->keyFree = keyFree;

141
    if (VIR_ALLOC_N(table->table, size) < 0) {
142
        virReportOOMError();
143 144
        VIR_FREE(table);
        return NULL;
D
Daniel Veillard 已提交
145
    }
146 147

    return table;
D
Daniel Veillard 已提交
148 149
}

150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169

/**
 * virHashCreate:
 * @size: the size of the hash table
 * @dataFree: callback to free data
 *
 * Create a new virHashTablePtr.
 *
 * Returns the newly created object, or NULL if an error occured.
 */
virHashTablePtr virHashCreate(int size, virHashDataFree dataFree)
{
    return virHashCreateFull(size,
                             dataFree,
                             virHashStrCode,
                             virHashStrEqual,
                             virHashStrCopy,
                             virHashStrFree);
}

D
Daniel Veillard 已提交
170
/**
171
 * virHashGrow:
D
Daniel Veillard 已提交
172 173 174 175 176 177 178 179
 * @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
180 181
virHashGrow(virHashTablePtr table, int size)
{
D
Daniel Veillard 已提交
182 183
    unsigned long key;
    int oldsize, i;
184 185
    virHashEntryPtr iter, next;
    struct _virHashEntry *oldtable;
186

D
Daniel Veillard 已提交
187 188 189
#ifdef DEBUG_GROW
    unsigned long nbElem = 0;
#endif
190

D
Daniel Veillard 已提交
191
    if (table == NULL)
192
        return (-1);
D
Daniel Veillard 已提交
193
    if (size < 8)
194
        return (-1);
D
Daniel Veillard 已提交
195
    if (size > 8 * 2048)
196
        return (-1);
D
Daniel Veillard 已提交
197 198 199 200

    oldsize = table->size;
    oldtable = table->table;
    if (oldtable == NULL)
201 202
        return (-1);

203
    if (VIR_ALLOC_N(table->table, size) < 0) {
204
        virReportOOMError();
205 206
        table->table = oldtable;
        return (-1);
D
Daniel Veillard 已提交
207 208 209
    }
    table->size = size;

210
    /* If the two loops are merged, there would be situations where
211
     * a new entry needs to allocated and data copied into it from
212 213 214 215
     * 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 已提交
216
    for (i = 0; i < oldsize; i++) {
217 218 219 220 221
        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 已提交
222 223 224
    }

    for (i = 0; i < oldsize; i++) {
225 226 227 228 229 230 231 232 233 234 235 236
        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;
237
                VIR_FREE(iter);
238 239 240 241
            } else {
                iter->next = table->table[key].next;
                table->table[key].next = iter;
            }
D
Daniel Veillard 已提交
242 243

#ifdef DEBUG_GROW
244
            nbElem++;
D
Daniel Veillard 已提交
245 246
#endif

247 248
            iter = next;
        }
D
Daniel Veillard 已提交
249 250
    }

251
    VIR_FREE(oldtable);
D
Daniel Veillard 已提交
252 253

#ifdef DEBUG_GROW
E
Eric Blake 已提交
254 255
    VIR_DEBUG("virHashGrow : from %d to %d, %ld elems\n", oldsize,
              size, nbElem);
D
Daniel Veillard 已提交
256 257
#endif

258
    return (0);
D
Daniel Veillard 已提交
259 260 261
}

/**
262
 * virHashFree:
D
Daniel Veillard 已提交
263 264 265
 * @table: the hash table
 *
 * Free the hash @table and its contents. The userdata is
266
 * deallocated with function provided at creation time.
D
Daniel Veillard 已提交
267 268
 */
void
269
virHashFree(virHashTablePtr table)
270
{
D
Daniel Veillard 已提交
271
    int i;
272 273
    virHashEntryPtr iter;
    virHashEntryPtr next;
D
Daniel Veillard 已提交
274 275 276 277
    int inside_table = 0;
    int nbElems;

    if (table == NULL)
278
        return;
D
Daniel Veillard 已提交
279
    if (table->table) {
280 281 282 283 284 285 286 287
        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;
288 289
                if ((table->dataFree != NULL) && (iter->payload != NULL))
                    table->dataFree(iter->payload, iter->name);
290 291
                if (table->keyFree)
                    table->keyFree(iter->name);
292 293
                iter->payload = NULL;
                if (!inside_table)
294
                    VIR_FREE(iter);
295 296 297 298 299
                nbElems--;
                inside_table = 0;
                iter = next;
            }
        }
300
        VIR_FREE(table->table);
D
Daniel Veillard 已提交
301
    }
302
    VIR_FREE(table);
D
Daniel Veillard 已提交
303 304
}

305
static int
306 307
virHashAddOrUpdateEntry(virHashTablePtr table, const void *name,
                        void *userdata,
308
                        bool is_update)
309
{
D
Daniel Veillard 已提交
310
    unsigned long key, len = 0;
311 312
    virHashEntryPtr entry;
    virHashEntryPtr insert;
313
    char *new_name;
314
    bool found;
D
Daniel Veillard 已提交
315 316

    if ((table == NULL) || (name == NULL))
317
        return (-1);
D
Daniel Veillard 已提交
318 319 320 321

    /*
     * Check for duplicate and insertion location.
     */
322
    found = false;
323
    key = virHashComputeKey(table, name);
D
Daniel Veillard 已提交
324
    if (table->table[key].valid == 0) {
325
        insert = NULL;
D
Daniel Veillard 已提交
326
    } else {
327 328
        for (insert = &(table->table[key]); insert->next != NULL;
             insert = insert->next) {
329
            if (table->keyEqual(insert->name, name)) {
330 331 332
                found = true;
                break;
            }
333 334
            len++;
        }
335
        if (table->keyEqual(insert->name, name))
336 337 338 339 340
            found = true;
    }

    if (found) {
        if (is_update) {
341 342
            if (table->dataFree)
                table->dataFree(insert->payload, insert->name);
343 344 345
            insert->payload = userdata;
            return (0);
        } else {
346
            return (-1);
347
        }
D
Daniel Veillard 已提交
348 349 350
    }

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

359
    new_name = table->keyCopy(name);
360
    if (new_name == NULL) {
361
        virReportOOMError();
362 363 364 365 366
        if (insert != NULL)
            VIR_FREE(entry);
        return (-1);
    }
    entry->name = new_name;
D
Daniel Veillard 已提交
367 368 369 370
    entry->payload = userdata;
    entry->next = NULL;
    entry->valid = 1;

371 372
    if (insert != NULL)
        insert->next = entry;
D
Daniel Veillard 已提交
373 374 375 376

    table->nbElems++;

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

379
    return (0);
D
Daniel Veillard 已提交
380 381
}

382 383 384 385 386 387 388 389 390 391 392 393
/**
 * virHashAddEntry:
 * @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
394
virHashAddEntry(virHashTablePtr table, const void *name, void *userdata)
395
{
396
    return virHashAddOrUpdateEntry(table, name, userdata, false);
397 398
}

D
Daniel Veillard 已提交
399
/**
400
 * virHashUpdateEntry:
D
Daniel Veillard 已提交
401 402 403 404 405 406 407 408 409 410 411
 * @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. 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
412
virHashUpdateEntry(virHashTablePtr table, const void *name,
413
                   void *userdata)
414
{
415
    return virHashAddOrUpdateEntry(table, name, userdata, true);
D
Daniel Veillard 已提交
416 417 418
}

/**
419
 * virHashLookup:
D
Daniel Veillard 已提交
420 421 422
 * @table: the hash table
 * @name: the name of the userdata
 *
423
 * Find the userdata specified by @name
D
Daniel Veillard 已提交
424 425 426 427
 *
 * Returns the a pointer to the userdata
 */
void *
428
virHashLookup(virHashTablePtr table, const void *name)
429
{
D
Daniel Veillard 已提交
430
    unsigned long key;
431
    virHashEntryPtr entry;
D
Daniel Veillard 已提交
432 433

    if (table == NULL)
434
        return (NULL);
D
Daniel Veillard 已提交
435
    if (name == NULL)
436
        return (NULL);
437
    key = virHashComputeKey(table, name);
D
Daniel Veillard 已提交
438
    if (table->table[key].valid == 0)
439
        return (NULL);
D
Daniel Veillard 已提交
440
    for (entry = &(table->table[key]); entry != NULL; entry = entry->next) {
441
        if (table->keyEqual(entry->name, name))
442
            return (entry->payload);
D
Daniel Veillard 已提交
443
    }
444
    return (NULL);
D
Daniel Veillard 已提交
445 446
}

447 448 449 450 451 452 453 454 455 456 457

/**
 * virHashSteal:
 * @table: the hash table
 * @name: the name of the userdata
 *
 * Find the userdata specified by @name
 * and remove it from the hash without freeing it.
 *
 * Returns the a pointer to the userdata
 */
458
void *virHashSteal(virHashTablePtr table, const void *name)
459 460 461 462 463 464 465 466 467 468 469 470
{
    void *data = virHashLookup(table, name);
    if (data) {
        virHashDataFree dataFree = table->dataFree;
        table->dataFree = NULL;
        virHashRemoveEntry(table, name);
        table->dataFree = dataFree;
    }
    return data;
}


D
Daniel Veillard 已提交
471
/**
472
 * virHashSize:
D
Daniel Veillard 已提交
473 474 475 476 477 478 479 480
 * @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
481 482
virHashSize(virHashTablePtr table)
{
D
Daniel Veillard 已提交
483
    if (table == NULL)
484 485
        return (-1);
    return (table->nbElems);
D
Daniel Veillard 已提交
486 487 488
}

/**
489
 * virHashRemoveEntry:
D
Daniel Veillard 已提交
490 491 492 493 494 495 496 497 498 499
 * @table: the hash table
 * @name: the name of the userdata
 *
 * 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
500
virHashRemoveEntry(virHashTablePtr table, const void *name)
501
{
D
Daniel Veillard 已提交
502
    unsigned long key;
503 504
    virHashEntryPtr entry;
    virHashEntryPtr prev = NULL;
D
Daniel Veillard 已提交
505 506

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

509
    key = virHashComputeKey(table, name);
D
Daniel Veillard 已提交
510
    if (table->table[key].valid == 0) {
511
        return (-1);
D
Daniel Veillard 已提交
512
    } else {
513 514
        for (entry = &(table->table[key]); entry != NULL;
             entry = entry->next) {
515
            if (table->keyEqual(entry->name, name)) {
516 517
                if (table->dataFree && (entry->payload != NULL))
                    table->dataFree(entry->payload, entry->name);
D
Daniel Veillard 已提交
518
                entry->payload = NULL;
519 520
                if (table->keyFree)
                    table->keyFree(entry->name);
521
                if (prev) {
D
Daniel Veillard 已提交
522
                    prev->next = entry->next;
523
                    VIR_FREE(entry);
524 525 526 527 528 529 530
                } else {
                    if (entry->next == NULL) {
                        entry->valid = 0;
                    } else {
                        entry = entry->next;
                        memcpy(&(table->table[key]), entry,
                               sizeof(virHashEntry));
531
                        VIR_FREE(entry);
532 533
                    }
                }
D
Daniel Veillard 已提交
534
                table->nbElems--;
535
                return (0);
D
Daniel Veillard 已提交
536 537 538
            }
            prev = entry;
        }
539
        return (-1);
D
Daniel Veillard 已提交
540 541
    }
}
542

543 544 545 546 547 548 549 550 551 552 553 554 555 556

/**
 * 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
 */
557
int virHashForEach(virHashTablePtr table, virHashIterator iter, void *data) {
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
    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
 */
590
int virHashRemoveSet(virHashTablePtr table, virHashSearcher iter, const void *data) {
591 592 593 594 595 596 597 598 599 600 601 602
    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++;
603 604
                if (table->dataFree)
                    table->dataFree(entry->payload, entry->name);
605 606
                if (table->keyFree)
                    table->keyFree(entry->name);
D
Daniel Veillard 已提交
607
                table->nbElems--;
608 609
                if (prev) {
                    prev->next = entry->next;
610
                    VIR_FREE(entry);
D
Daniel Veillard 已提交
611
                    entry = prev;
612 613 614 615 616 617 618 619
                } else {
                    if (entry->next == NULL) {
                        entry->valid = 0;
                        entry->name = NULL;
                    } else {
                        entry = entry->next;
                        memcpy(&(table->table[i]), entry,
                               sizeof(virHashEntry));
620
                        VIR_FREE(entry);
D
Daniel Veillard 已提交
621 622
                        entry = &(table->table[i]);
                        continue;
623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663
                    }
                }
            }
            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);
}