virhash.c 18.3 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
 *
E
Eric Blake 已提交
6
 * Copyright (C) 2005-2014 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.
 */

19
#include <config.h>
J
Jim Meyering 已提交
20

21

22
#include "virerror.h"
23
#include "virhash.h"
24
#include "viralloc.h"
25
#include "virlog.h"
26 27
#include "virhashcode.h"
#include "virrandom.h"
28
#include "virstring.h"
J
Jiri Denemark 已提交
29
#include "virobject.h"
D
Daniel Veillard 已提交
30

31 32
#define VIR_FROM_THIS VIR_FROM_NONE

33 34
VIR_LOG_INIT("util.hash");

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

/* #define DEBUG_GROW */

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

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

J
Jiri Denemark 已提交
65 66 67 68 69 70 71 72 73 74
struct _virHashAtomic {
    virObjectLockable parent;
    virHashTablePtr hash;
};

static virClassPtr virHashAtomicClass;
static void virHashAtomicDispose(void *obj);

static int virHashAtomicOnceInit(void)
{
75
    if (!VIR_CLASS_NEW(virHashAtomic, virClassForObjectLockable()))
J
Jiri Denemark 已提交
76
        return -1;
77 78

    return 0;
J
Jiri Denemark 已提交
79
}
80

81
VIR_ONCE_GLOBAL_INIT(virHashAtomic);
J
Jiri Denemark 已提交
82 83


84
static uint32_t virHashStrCode(const void *name, uint32_t seed)
85
{
86
    return virHashCodeGen(name, strlen(name), seed);
87 88 89 90 91 92 93 94 95
}

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

static void *virHashStrCopy(const void *name)
{
96 97 98
    char *ret;
    ignore_value(VIR_STRDUP(ret, name));
    return ret;
99 100 101 102 103 104 105 106
}

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


E
Eric Blake 已提交
107 108 109 110 111 112 113
void
virHashValueFree(void *value, const void *name ATTRIBUTE_UNUSED)
{
    VIR_FREE(value);
}


114
static size_t
E
Eric Blake 已提交
115
virHashComputeKey(const virHashTable *table, const void *name)
116
{
117
    uint32_t value = table->keyCode(name, table->seed);
118
    return value % table->size;
D
Daniel Veillard 已提交
119 120 121
}

/**
122
 * virHashCreateFull:
D
Daniel Veillard 已提交
123
 * @size: the size of the hash table
124 125 126 127 128
 * @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 已提交
129
 *
130
 * Create a new virHashTablePtr.
D
Daniel Veillard 已提交
131
 *
E
Eric Blake 已提交
132
 * Returns the newly created object, or NULL if an error occurred.
D
Daniel Veillard 已提交
133
 */
134
virHashTablePtr virHashCreateFull(ssize_t size,
135 136 137 138 139
                                  virHashDataFree dataFree,
                                  virHashKeyCode keyCode,
                                  virHashKeyEqual keyEqual,
                                  virHashKeyCopy keyCopy,
                                  virHashKeyFree keyFree)
140
{
141
    virHashTablePtr table = NULL;
142

D
Daniel Veillard 已提交
143 144
    if (size <= 0)
        size = 256;
145

146
    if (VIR_ALLOC(table) < 0)
147 148
        return NULL;

149
    table->seed = virRandomBits(32);
150 151
    table->size = size;
    table->nbElems = 0;
152
    table->dataFree = dataFree;
153 154 155 156 157
    table->keyCode = keyCode;
    table->keyEqual = keyEqual;
    table->keyCopy = keyCopy;
    table->keyFree = keyFree;

158 159 160
    if (VIR_ALLOC_N(table->table, size) < 0) {
        VIR_FREE(table);
        return NULL;
D
Daniel Veillard 已提交
161
    }
162 163

    return table;
D
Daniel Veillard 已提交
164 165
}

166 167 168 169 170 171 172 173

/**
 * virHashCreate:
 * @size: the size of the hash table
 * @dataFree: callback to free data
 *
 * Create a new virHashTablePtr.
 *
174
 * Returns the newly created object, or NULL if an error occurred.
175
 */
176
virHashTablePtr virHashCreate(ssize_t size, virHashDataFree dataFree)
177 178 179 180 181 182 183 184 185
{
    return virHashCreateFull(size,
                             dataFree,
                             virHashStrCode,
                             virHashStrEqual,
                             virHashStrCopy,
                             virHashStrFree);
}

J
Jiri Denemark 已提交
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215

virHashAtomicPtr
virHashAtomicNew(ssize_t size,
                 virHashDataFree dataFree)
{
    virHashAtomicPtr hash;

    if (virHashAtomicInitialize() < 0)
        return NULL;

    if (!(hash = virObjectLockableNew(virHashAtomicClass)))
        return NULL;

    if (!(hash->hash = virHashCreate(size, dataFree))) {
        virObjectUnref(hash);
        return NULL;
    }
    return hash;
}


static void
virHashAtomicDispose(void *obj)
{
    virHashAtomicPtr hash = obj;

    virHashFree(hash->hash);
}


D
Daniel Veillard 已提交
216
/**
217
 * virHashGrow:
D
Daniel Veillard 已提交
218 219 220 221 222 223 224 225
 * @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
226
virHashGrow(virHashTablePtr table, size_t size)
227
{
228
    size_t oldsize, i;
229
    virHashEntryPtr *oldtable;
230

D
Daniel Veillard 已提交
231
#ifdef DEBUG_GROW
232
    size_t nbElem = 0;
D
Daniel Veillard 已提交
233
#endif
234

D
Daniel Veillard 已提交
235
    if (table == NULL)
236
        return -1;
D
Daniel Veillard 已提交
237
    if (size < 8)
238
        return -1;
D
Daniel Veillard 已提交
239
    if (size > 8 * 2048)
240
        return -1;
D
Daniel Veillard 已提交
241 242 243 244

    oldsize = table->size;
    oldtable = table->table;
    if (oldtable == NULL)
245
        return -1;
246

247
    if (VIR_ALLOC_N(table->table, size) < 0) {
248
        table->table = oldtable;
249
        return -1;
D
Daniel Veillard 已提交
250 251 252 253
    }
    table->size = size;

    for (i = 0; i < oldsize; i++) {
254
        virHashEntryPtr iter = oldtable[i];
255
        while (iter) {
256
            virHashEntryPtr next = iter->next;
257
            size_t key = virHashComputeKey(table, iter->name);
258

259 260
            iter->next = table->table[key];
            table->table[key] = iter;
D
Daniel Veillard 已提交
261 262

#ifdef DEBUG_GROW
263
            nbElem++;
D
Daniel Veillard 已提交
264
#endif
265 266
            iter = next;
        }
D
Daniel Veillard 已提交
267 268
    }

269
    VIR_FREE(oldtable);
D
Daniel Veillard 已提交
270 271

#ifdef DEBUG_GROW
J
Jiri Denemark 已提交
272
    VIR_DEBUG("virHashGrow : from %d to %d, %ld elems", oldsize,
E
Eric Blake 已提交
273
              size, nbElem);
D
Daniel Veillard 已提交
274 275
#endif

276
    return 0;
D
Daniel Veillard 已提交
277 278 279
}

/**
280
 * virHashFree:
D
Daniel Veillard 已提交
281 282 283
 * @table: the hash table
 *
 * Free the hash @table and its contents. The userdata is
284
 * deallocated with function provided at creation time.
D
Daniel Veillard 已提交
285 286
 */
void
287
virHashFree(virHashTablePtr table)
288
{
289
    size_t i;
D
Daniel Veillard 已提交
290 291

    if (table == NULL)
292
        return;
293 294 295 296 297 298 299 300 301 302 303 304

    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;
305
        }
D
Daniel Veillard 已提交
306
    }
307

E
Eric Blake 已提交
308
    VIR_FREE(table->table);
309
    VIR_FREE(table);
D
Daniel Veillard 已提交
310 311
}

312
static int
313 314
virHashAddOrUpdateEntry(virHashTablePtr table, const void *name,
                        void *userdata,
315
                        bool is_update)
316
{
317
    size_t key, len = 0;
318
    virHashEntryPtr entry;
319
    virHashEntryPtr last = NULL;
320
    void *new_name;
D
Daniel Veillard 已提交
321 322

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

325
    key = virHashComputeKey(table, name);
D
Daniel Veillard 已提交
326

327 328 329 330 331 332 333 334 335
    /* 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 {
336 337
                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                               _("Duplicate key"));
338 339
                return -1;
            }
340
        }
341
        last = entry;
342
        len++;
D
Daniel Veillard 已提交
343 344
    }

345 346 347
    if (VIR_ALLOC(entry) < 0 || !(new_name = table->keyCopy(name))) {
        VIR_FREE(entry);
        return -1;
348
    }
349

350
    entry->name = new_name;
D
Daniel Veillard 已提交
351
    entry->payload = userdata;
352 353 354 355 356

    if (last)
        last->next = entry;
    else
        table->table[key] = entry;
D
Daniel Veillard 已提交
357 358 359 360

    table->nbElems++;

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

363
    return 0;
D
Daniel Veillard 已提交
364 365
}

366 367 368 369 370 371 372 373 374 375 376 377
/**
 * 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
378
virHashAddEntry(virHashTablePtr table, const void *name, void *userdata)
379
{
380
    return virHashAddOrUpdateEntry(table, name, userdata, false);
381 382
}

D
Daniel Veillard 已提交
383
/**
384
 * virHashUpdateEntry:
D
Daniel Veillard 已提交
385 386 387 388 389 390 391 392 393 394 395
 * @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
396
virHashUpdateEntry(virHashTablePtr table, const void *name,
397
                   void *userdata)
398
{
399
    return virHashAddOrUpdateEntry(table, name, userdata, true);
D
Daniel Veillard 已提交
400 401
}

J
Jiri Denemark 已提交
402 403 404 405 406 407 408 409 410 411 412 413 414 415 416
int
virHashAtomicUpdate(virHashAtomicPtr table,
                    const void *name,
                    void *userdata)
{
    int ret;

    virObjectLock(table);
    ret = virHashAddOrUpdateEntry(table->hash, name, userdata, true);
    virObjectUnlock(table);

    return ret;
}


D
Daniel Veillard 已提交
417
/**
418
 * virHashLookup:
D
Daniel Veillard 已提交
419 420 421
 * @table: the hash table
 * @name: the name of the userdata
 *
422
 * Find the userdata specified by @name
D
Daniel Veillard 已提交
423
 *
424
 * Returns a pointer to the userdata
D
Daniel Veillard 已提交
425 426
 */
void *
E
Eric Blake 已提交
427
virHashLookup(const virHashTable *table, const void *name)
428
{
429
    size_t key;
430
    virHashEntryPtr entry;
D
Daniel Veillard 已提交
431

432 433 434
    if (!table || !name)
        return NULL;

435
    key = virHashComputeKey(table, name);
436
    for (entry = table->table[key]; entry; entry = entry->next) {
437
        if (table->keyEqual(entry->name, name))
438
            return entry->payload;
D
Daniel Veillard 已提交
439
    }
440
    return NULL;
D
Daniel Veillard 已提交
441 442
}

443 444 445 446 447 448 449 450 451

/**
 * 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.
 *
452
 * Returns a pointer to the userdata
453
 */
454
void *virHashSteal(virHashTablePtr table, const void *name)
455 456 457 458 459 460 461 462 463 464 465
{
    void *data = virHashLookup(table, name);
    if (data) {
        virHashDataFree dataFree = table->dataFree;
        table->dataFree = NULL;
        virHashRemoveEntry(table, name);
        table->dataFree = dataFree;
    }
    return data;
}

J
Jiri Denemark 已提交
466 467 468 469 470 471 472 473 474 475 476 477 478
void *
virHashAtomicSteal(virHashAtomicPtr table,
                   const void *name)
{
    void *data;

    virObjectLock(table);
    data = virHashSteal(table->hash, name);
    virObjectUnlock(table);

    return data;
}

479

D
Daniel Veillard 已提交
480
/**
481
 * virHashSize:
D
Daniel Veillard 已提交
482 483 484 485 486 487 488
 * @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
 */
489
ssize_t
E
Eric Blake 已提交
490
virHashSize(const virHashTable *table)
491
{
D
Daniel Veillard 已提交
492
    if (table == NULL)
493 494
        return -1;
    return table->nbElems;
D
Daniel Veillard 已提交
495 496
}

497 498 499 500 501 502 503 504 505
/**
 * 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
 */
506
ssize_t
E
Eric Blake 已提交
507
virHashTableSize(const virHashTable *table)
508 509 510 511 512 513 514
{
    if (table == NULL)
        return -1;
    return table->size;
}


D
Daniel Veillard 已提交
515
/**
516
 * virHashRemoveEntry:
D
Daniel Veillard 已提交
517 518 519 520 521 522 523 524 525 526
 * @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
527
virHashRemoveEntry(virHashTablePtr table, const void *name)
528
{
529
    virHashEntryPtr entry;
530
    virHashEntryPtr *nextptr;
D
Daniel Veillard 已提交
531 532

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

535 536 537 538 539 540 541 542 543 544 545
    nextptr = table->table + virHashComputeKey(table, name);
    for (entry = *nextptr; entry; entry = entry->next) {
        if (table->keyEqual(entry->name, name)) {
            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 已提交
546
        }
547
        nextptr = &entry->next;
D
Daniel Veillard 已提交
548
    }
549 550

    return -1;
D
Daniel Veillard 已提交
551
}
552

553 554 555 556 557 558 559 560

/**
 * 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
561 562
 * 'iter' callback. The callback is allowed to remove the current element
 * using virHashRemoveEntry but calling other virHash* functions is prohibited.
563 564
 * If @iter fails and returns a negative value, the evaluation is stopped and -1
 * is returned.
565
 *
566
 * Returns 0 on success or -1 on failure.
567
 */
568
int
569
virHashForEach(virHashTablePtr table, virHashIterator iter, void *data)
570
{
571 572
    size_t i;
    int ret = -1;
573 574

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

577
    for (i = 0; i < table->size; i++) {
578
        virHashEntryPtr entry = table->table[i];
579
        while (entry) {
580
            virHashEntryPtr next = entry->next;
581
            ret = iter(entry->payload, entry->name, data);
582

583 584 585
            if (ret < 0)
                goto cleanup;

586
            entry = next;
587 588
        }
    }
589

590 591 592
    ret = 0;
 cleanup:
    return ret;
593 594
}

595

596 597 598 599 600 601 602 603 604
/**
 * 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 已提交
605
 * data freer callback registered at creation.
606 607 608
 *
 * Returns number of items removed on success, -1 on failure
 */
609 610 611 612
ssize_t
virHashRemoveSet(virHashTablePtr table,
                 virHashSearcher iter,
                 const void *data)
613
{
614
    size_t i, count = 0;
615 616

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

619
    for (i = 0; i < table->size; i++) {
620
        virHashEntryPtr *nextptr = table->table + i;
621

622 623 624
        while (*nextptr) {
            virHashEntryPtr entry = *nextptr;
            if (!iter(entry->payload, entry->name, data)) {
E
Eric Blake 已提交
625
                nextptr = &entry->next;
626
            } else {
627
                count++;
628 629
                if (table->dataFree)
                    table->dataFree(entry->payload, entry->name);
630 631
                if (table->keyFree)
                    table->keyFree(entry->name);
632 633
                *nextptr = entry->next;
                VIR_FREE(entry);
D
Daniel Veillard 已提交
634
                table->nbElems--;
635 636 637
            }
        }
    }
638

639
    return count;
640 641
}

642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666
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);
}

667 668 669 670 671
/**
 * virHashSearch:
 * @table: the hash table to search
 * @iter: an iterator to identify the desired element
 * @data: extra opaque information passed to the iter
672
 * @name: the name of found user data, pass NULL to ignore
673 674 675 676
 *
 * 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.
677 678
 * The elements are processed in a undefined order. Caller is
 * responsible for freeing the @name.
679
 */
E
Eric Blake 已提交
680
void *virHashSearch(const virHashTable *ctable,
681
                    virHashSearcher iter,
682 683
                    const void *data,
                    void **name)
684
{
685
    size_t i;
686

E
Eric Blake 已提交
687 688 689
    /* Cast away const for internal detection of misuse.  */
    virHashTablePtr table = (virHashTablePtr)ctable;

690
    if (table == NULL || iter == NULL)
691
        return NULL;
692

693
    for (i = 0; i < table->size; i++) {
694 695 696
        virHashEntryPtr entry;
        for (entry = table->table[i]; entry; entry = entry->next) {
            if (iter(entry->payload, entry->name, data)) {
697 698
                if (name)
                    *name = table->keyCopy(entry->name);
699
                return entry->payload;
700 701 702
            }
        }
    }
703

704
    return NULL;
705
}
706 707 708 709

struct getKeysIter
{
    virHashKeyValuePair *sortArray;
710
    size_t arrayIdx;
711 712
};

713 714
static int virHashGetKeysIterator(void *payload,
                                  const void *key, void *data)
715 716 717 718 719 720 721
{
    struct getKeysIter *iter = data;

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

    iter->arrayIdx++;
722
    return 0;
723 724 725 726 727 728 729
}

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

virHashKeyValuePairPtr virHashGetItems(virHashTablePtr table,
                                       virHashKeyComparator compar)
{
730
    ssize_t numElems = virHashSize(table);
731 732 733 734 735 736 737 738
    struct getKeysIter iter = {
        .arrayIdx = 0,
        .sortArray = NULL,
    };

    if (numElems < 0)
        return NULL;

739
    if (VIR_ALLOC_N(iter.sortArray, numElems + 1))
740 741 742 743 744 745 746 747 748 749
        return NULL;

    virHashForEach(table, virHashGetKeysIterator, &iter);

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

    return iter.sortArray;
}
750 751 752 753

struct virHashEqualData
{
    bool equal;
E
Eric Blake 已提交
754
    const virHashTable *table2;
755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774
    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;
}

E
Eric Blake 已提交
775 776
bool virHashEqual(const virHashTable *table1,
                  const virHashTable *table2,
777 778 779 780 781 782 783 784 785 786 787 788 789 790 791
                  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;

792
    virHashSearch(table1, virHashEqualSearcher, &data, NULL);
793 794 795

    return data.equal;
}