virhash.c 17.6 KB
Newer Older
D
Daniel Veillard 已提交
1
/*
E
Eric Blake 已提交
2
 * virhash.c: chained hash tables
D
Daniel Veillard 已提交
3 4 5
 *
 * Reference: Your favorite introductory book on algorithms
 *
6
 * Copyright (C) 2005-2012 Red Hat, Inc.
D
Daniel Veillard 已提交
7 8 9 10 11 12 13 14 15 16 17
 * 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.
 *
18
 * Author: Bjorn Reese <bjorn.reese@systematic.dk>
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"
28
#include "virhash.h"
29
#include "memory.h"
E
Eric Blake 已提交
30
#include "logging.h"
31 32
#include "virhashcode.h"
#include "virrandom.h"
D
Daniel Veillard 已提交
33

34 35
#define VIR_FROM_THIS VIR_FROM_NONE

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

/* #define DEBUG_GROW */

40 41
#define virHashIterationError(ret)                                      \
    do {                                                                \
42
        VIR_ERROR(_("Hash operation not allowed during iteration"));   \
43 44 45
        return ret;                                                     \
    } while (0)

D
Daniel Veillard 已提交
46 47 48
/*
 * A single entry in the hash table
 */
49 50 51 52
typedef struct _virHashEntry virHashEntry;
typedef virHashEntry *virHashEntryPtr;
struct _virHashEntry {
    struct _virHashEntry *next;
53
    void *name;
D
Daniel Veillard 已提交
54 55 56 57 58 59
    void *payload;
};

/*
 * The entire hash table
 */
60
struct _virHashTable {
61
    virHashEntryPtr *table;
62
    uint32_t seed;
63 64
    size_t size;
    size_t nbElems;
65 66 67 68
    /* True iff we are iterating over hash entries. */
    bool iterating;
    /* Pointer to the current entry during iteration. */
    virHashEntryPtr current;
69
    virHashDataFree dataFree;
70 71 72 73
    virHashKeyCode keyCode;
    virHashKeyEqual keyEqual;
    virHashKeyCopy keyCopy;
    virHashKeyFree keyFree;
D
Daniel Veillard 已提交
74 75
};

76
static uint32_t virHashStrCode(const void *name, uint32_t seed)
77
{
78
    return virHashCodeGen(name, strlen(name), seed);
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
}

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);
}


97
static size_t
98 99
virHashComputeKey(virHashTablePtr table, const void *name)
{
100
    uint32_t value = table->keyCode(name, table->seed);
101
    return value % table->size;
D
Daniel Veillard 已提交
102 103 104
}

/**
105
 * virHashCreateFull:
D
Daniel Veillard 已提交
106
 * @size: the size of the hash table
107 108 109 110 111
 * @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 已提交
112
 *
113
 * Create a new virHashTablePtr.
D
Daniel Veillard 已提交
114
 *
E
Eric Blake 已提交
115
 * Returns the newly created object, or NULL if an error occurred.
D
Daniel Veillard 已提交
116
 */
117
virHashTablePtr virHashCreateFull(ssize_t size,
118 119 120 121 122
                                  virHashDataFree dataFree,
                                  virHashKeyCode keyCode,
                                  virHashKeyEqual keyEqual,
                                  virHashKeyCopy keyCopy,
                                  virHashKeyFree keyFree)
123
{
124
    virHashTablePtr table = NULL;
125

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

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

134
    table->seed = virRandomBits(32);
135 136
    table->size = size;
    table->nbElems = 0;
137
    table->dataFree = dataFree;
138 139 140 141 142
    table->keyCode = keyCode;
    table->keyEqual = keyEqual;
    table->keyCopy = keyCopy;
    table->keyFree = keyFree;

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

    return table;
D
Daniel Veillard 已提交
150 151
}

152 153 154 155 156 157 158 159

/**
 * virHashCreate:
 * @size: the size of the hash table
 * @dataFree: callback to free data
 *
 * Create a new virHashTablePtr.
 *
160
 * Returns the newly created object, or NULL if an error occurred.
161
 */
162
virHashTablePtr virHashCreate(ssize_t size, virHashDataFree dataFree)
163 164 165 166 167 168 169 170 171
{
    return virHashCreateFull(size,
                             dataFree,
                             virHashStrCode,
                             virHashStrEqual,
                             virHashStrCopy,
                             virHashStrFree);
}

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

D
Daniel Veillard 已提交
187
#ifdef DEBUG_GROW
188
    size_t nbElem = 0;
D
Daniel Veillard 已提交
189
#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
        return -1;
202

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

    for (i = 0; i < oldsize; i++) {
211
        virHashEntryPtr iter = oldtable[i];
212
        while (iter) {
213
            virHashEntryPtr next = iter->next;
214
            size_t key = virHashComputeKey(table, iter->name);
215

216 217
            iter->next = table->table[key];
            table->table[key] = iter;
D
Daniel Veillard 已提交
218 219

#ifdef DEBUG_GROW
220
            nbElem++;
D
Daniel Veillard 已提交
221
#endif
222 223
            iter = next;
        }
D
Daniel Veillard 已提交
224 225
    }

226
    VIR_FREE(oldtable);
D
Daniel Veillard 已提交
227 228

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

233
    return 0;
D
Daniel Veillard 已提交
234 235 236
}

/**
237
 * virHashFree:
D
Daniel Veillard 已提交
238 239 240
 * @table: the hash table
 *
 * Free the hash @table and its contents. The userdata is
241
 * deallocated with function provided at creation time.
D
Daniel Veillard 已提交
242 243
 */
void
244
virHashFree(virHashTablePtr table)
245
{
246
    size_t i;
D
Daniel Veillard 已提交
247 248

    if (table == NULL)
249
        return;
250 251 252 253 254 255 256 257 258 259 260 261

    for (i = 0; i < table->size; i++) {
        virHashEntryPtr iter = table->table[i];
        while (iter) {
            virHashEntryPtr next = iter->next;

            if (table->dataFree)
                table->dataFree(iter->payload, iter->name);
            if (table->keyFree)
                table->keyFree(iter->name);
            VIR_FREE(iter);
            iter = next;
262
        }
D
Daniel Veillard 已提交
263
    }
264

E
Eric Blake 已提交
265
    VIR_FREE(table->table);
266
    VIR_FREE(table);
D
Daniel Veillard 已提交
267 268
}

269
static int
270 271
virHashAddOrUpdateEntry(virHashTablePtr table, const void *name,
                        void *userdata,
272
                        bool is_update)
273
{
274
    size_t key, len = 0;
275
    virHashEntryPtr entry;
276
    char *new_name;
D
Daniel Veillard 已提交
277 278

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

281 282 283
    if (table->iterating)
        virHashIterationError(-1);

284
    key = virHashComputeKey(table, name);
D
Daniel Veillard 已提交
285

286 287 288 289 290 291 292 293 294 295 296
    /* Check for duplicate entry */
    for (entry = table->table[key]; entry; entry = entry->next) {
        if (table->keyEqual(entry->name, name)) {
            if (is_update) {
                if (table->dataFree)
                    table->dataFree(entry->payload, entry->name);
                entry->payload = userdata;
                return 0;
            } else {
                return -1;
            }
297
        }
298
        len++;
D
Daniel Veillard 已提交
299 300
    }

301
    if (VIR_ALLOC(entry) < 0 || !(new_name = table->keyCopy(name))) {
302
        virReportOOMError();
303 304
        VIR_FREE(entry);
        return -1;
305
    }
306

307
    entry->name = new_name;
D
Daniel Veillard 已提交
308
    entry->payload = userdata;
309 310
    entry->next = table->table[key];
    table->table[key] = entry;
D
Daniel Veillard 已提交
311 312 313 314

    table->nbElems++;

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

317
    return 0;
D
Daniel Veillard 已提交
318 319
}

320 321 322 323 324 325 326 327 328 329 330 331
/**
 * 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
332
virHashAddEntry(virHashTablePtr table, const void *name, void *userdata)
333
{
334
    return virHashAddOrUpdateEntry(table, name, userdata, false);
335 336
}

D
Daniel Veillard 已提交
337
/**
338
 * virHashUpdateEntry:
D
Daniel Veillard 已提交
339 340 341 342 343 344 345 346 347 348 349
 * @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
350
virHashUpdateEntry(virHashTablePtr table, const void *name,
351
                   void *userdata)
352
{
353
    return virHashAddOrUpdateEntry(table, name, userdata, true);
D
Daniel Veillard 已提交
354 355 356
}

/**
357
 * virHashLookup:
D
Daniel Veillard 已提交
358 359 360
 * @table: the hash table
 * @name: the name of the userdata
 *
361
 * Find the userdata specified by @name
D
Daniel Veillard 已提交
362 363 364 365
 *
 * Returns the a pointer to the userdata
 */
void *
366
virHashLookup(virHashTablePtr table, const void *name)
367
{
368
    size_t key;
369
    virHashEntryPtr entry;
D
Daniel Veillard 已提交
370

371 372 373
    if (!table || !name)
        return NULL;

374
    key = virHashComputeKey(table, name);
375
    for (entry = table->table[key]; entry; entry = entry->next) {
376
        if (table->keyEqual(entry->name, name))
377
            return entry->payload;
D
Daniel Veillard 已提交
378
    }
379
    return NULL;
D
Daniel Veillard 已提交
380 381
}

382 383 384 385 386 387 388 389 390 391 392

/**
 * 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
 */
393
void *virHashSteal(virHashTablePtr table, const void *name)
394 395 396 397 398 399 400 401 402 403 404 405
{
    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 已提交
406
/**
407
 * virHashSize:
D
Daniel Veillard 已提交
408 409 410 411 412 413 414
 * @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
 */
415
ssize_t
416 417
virHashSize(virHashTablePtr table)
{
D
Daniel Veillard 已提交
418
    if (table == NULL)
419 420
        return -1;
    return table->nbElems;
D
Daniel Veillard 已提交
421 422
}

423 424 425 426 427 428 429 430 431
/**
 * virHashTableSize:
 * @table: the hash table
 *
 * Query the size of the hash @table, i.e., number of buckets in the table.
 *
 * Returns the number of keys in the hash table or
 * -1 in case of error
 */
432
ssize_t
433 434 435 436 437 438 439 440
virHashTableSize(virHashTablePtr table)
{
    if (table == NULL)
        return -1;
    return table->size;
}


D
Daniel Veillard 已提交
441
/**
442
 * virHashRemoveEntry:
D
Daniel Veillard 已提交
443 444 445 446 447 448 449 450 451 452
 * @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
453
virHashRemoveEntry(virHashTablePtr table, const void *name)
454
{
455
    virHashEntryPtr entry;
456
    virHashEntryPtr *nextptr;
D
Daniel Veillard 已提交
457 458

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

461 462 463 464 465 466 467 468 469 470 471 472 473 474
    nextptr = table->table + virHashComputeKey(table, name);
    for (entry = *nextptr; entry; entry = entry->next) {
        if (table->keyEqual(entry->name, name)) {
            if (table->iterating && table->current != entry)
                virHashIterationError(-1);

            if (table->dataFree)
                table->dataFree(entry->payload, entry->name);
            if (table->keyFree)
                table->keyFree(entry->name);
            *nextptr = entry->next;
            VIR_FREE(entry);
            table->nbElems--;
            return 0;
D
Daniel Veillard 已提交
475
        }
476
        nextptr = &entry->next;
D
Daniel Veillard 已提交
477
    }
478 479

    return -1;
D
Daniel Veillard 已提交
480
}
481

482 483 484 485 486 487 488 489

/**
 * 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
490 491
 * 'iter' callback. The callback is allowed to remove the current element
 * using virHashRemoveEntry but calling other virHash* functions is prohibited.
492 493 494
 *
 * Returns number of items iterated over upon completion, -1 on failure
 */
495 496
ssize_t
virHashForEach(virHashTablePtr table, virHashIterator iter, void *data)
497
{
498
    size_t i, count = 0;
499 500

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

503 504 505 506 507
    if (table->iterating)
        virHashIterationError(-1);

    table->iterating = true;
    table->current = NULL;
508
    for (i = 0 ; i < table->size ; i++) {
509
        virHashEntryPtr entry = table->table[i];
510
        while (entry) {
511
            virHashEntryPtr next = entry->next;
512

513 514 515
            table->current = entry;
            iter(entry->payload, entry->name, data);
            table->current = NULL;
516

517
            count++;
518
            entry = next;
519 520
        }
    }
521 522
    table->iterating = false;

523
    return count;
524 525 526 527 528 529 530 531 532 533 534
}

/**
 * virHashRemoveSet
 * @table: the hash table to process
 * @iter: callback to identify elements for removal
 * @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
E
Eric Blake 已提交
535
 * data freer callback registered at creation.
536 537 538
 *
 * Returns number of items removed on success, -1 on failure
 */
539 540 541 542
ssize_t
virHashRemoveSet(virHashTablePtr table,
                 virHashSearcher iter,
                 const void *data)
543
{
544
    size_t i, count = 0;
545 546

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

549 550 551 552 553
    if (table->iterating)
        virHashIterationError(-1);

    table->iterating = true;
    table->current = NULL;
554
    for (i = 0 ; i < table->size ; i++) {
555
        virHashEntryPtr *nextptr = table->table + i;
556

557 558 559
        while (*nextptr) {
            virHashEntryPtr entry = *nextptr;
            if (!iter(entry->payload, entry->name, data)) {
E
Eric Blake 已提交
560
                nextptr = &entry->next;
561
            } else {
562
                count++;
563 564
                if (table->dataFree)
                    table->dataFree(entry->payload, entry->name);
565 566
                if (table->keyFree)
                    table->keyFree(entry->name);
567 568
                *nextptr = entry->next;
                VIR_FREE(entry);
D
Daniel Veillard 已提交
569
                table->nbElems--;
570 571 572
            }
        }
    }
573 574
    table->iterating = false;

575
    return count;
576 577
}

578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602
static int
_virHashRemoveAllIter(const void *payload ATTRIBUTE_UNUSED,
                      const void *name ATTRIBUTE_UNUSED,
                      const void *data ATTRIBUTE_UNUSED)
{
    return 1;
}

/**
 * virHashRemoveAll
 * @table: the hash table to clear
 *
 * Free the hash @table's contents. The userdata is
 * deallocated with the function provided at creation time.
 *
 * Returns the number of items removed on success, -1 on failure
 */
ssize_t
virHashRemoveAll(virHashTablePtr table)
{
    return virHashRemoveSet(table,
                            _virHashRemoveAllIter,
                            NULL);
}

603 604 605 606 607 608 609 610 611 612 613
/**
 * 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
 */
614 615 616 617
void *virHashSearch(virHashTablePtr table,
                    virHashSearcher iter,
                    const void *data)
{
618
    size_t i;
619 620

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

623 624 625 626 627
    if (table->iterating)
        virHashIterationError(NULL);

    table->iterating = true;
    table->current = NULL;
628
    for (i = 0 ; i < table->size ; i++) {
629 630 631 632 633
        virHashEntryPtr entry;
        for (entry = table->table[i]; entry; entry = entry->next) {
            if (iter(entry->payload, entry->name, data)) {
                table->iterating = false;
                return entry->payload;
634 635 636
            }
        }
    }
637 638
    table->iterating = false;

639
    return NULL;
640
}
641 642 643 644

struct getKeysIter
{
    virHashKeyValuePair *sortArray;
645
    size_t arrayIdx;
646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663
};

static void virHashGetKeysIterator(void *payload,
                                   const void *key, void *data)
{
    struct getKeysIter *iter = data;

    iter->sortArray[iter->arrayIdx].key = key;
    iter->sortArray[iter->arrayIdx].value = payload;

    iter->arrayIdx++;
}

typedef int (*qsort_comp)(const void *, const void *);

virHashKeyValuePairPtr virHashGetItems(virHashTablePtr table,
                                       virHashKeyComparator compar)
{
664
    ssize_t numElems = virHashSize(table);
665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685
    struct getKeysIter iter = {
        .arrayIdx = 0,
        .sortArray = NULL,
    };

    if (numElems < 0)
        return NULL;

    if (VIR_ALLOC_N(iter.sortArray, numElems + 1)) {
        virReportOOMError();
        return NULL;
    }

    virHashForEach(table, virHashGetKeysIterator, &iter);

    if (compar)
        qsort(&iter.sortArray[0], numElems, sizeof(iter.sortArray[0]),
              (qsort_comp)compar);

    return iter.sortArray;
}
686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731

struct virHashEqualData
{
    bool equal;
    const virHashTablePtr table2;
    virHashValueComparator compar;
};

static int virHashEqualSearcher(const void *payload, const void *name,
                                const void *data)
{
    struct virHashEqualData *vhed = (void *)data;
    const void *value;

    value = virHashLookup(vhed->table2, name);
    if (!value ||
        vhed->compar(value, payload) != 0) {
        /* key is missing in 2nd table or values are different */
        vhed->equal = false;
        /* stop 'iteration' */
        return 1;
    }
    return 0;
}

bool virHashEqual(const virHashTablePtr table1,
                  const virHashTablePtr table2,
                  virHashValueComparator compar)
{
    struct virHashEqualData data = {
        .equal = true,
        .table2 = table2,
        .compar = compar,
    };

    if (table1 == table2)
        return true;

    if (!table1 || !table2 ||
        virHashSize(table1) != virHashSize(table2))
        return false;

    virHashSearch(table1, virHashEqualSearcher, &data);

    return data.equal;
}