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
    void *new_name;
D
Daniel Veillard 已提交
320 321

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

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

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

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

348
    entry->name = new_name;
D
Daniel Veillard 已提交
349
    entry->payload = userdata;
350 351
    entry->next = table->table[key];
    table->table[key] = entry;
D
Daniel Veillard 已提交
352 353 354 355

    table->nbElems++;

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

358
    return 0;
D
Daniel Veillard 已提交
359 360
}

361 362 363 364 365 366 367 368 369 370 371 372
/**
 * 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
373
virHashAddEntry(virHashTablePtr table, const void *name, void *userdata)
374
{
375
    return virHashAddOrUpdateEntry(table, name, userdata, false);
376 377
}

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

J
Jiri Denemark 已提交
397 398 399 400 401 402 403 404 405 406 407 408 409 410 411
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 已提交
412
/**
413
 * virHashLookup:
D
Daniel Veillard 已提交
414 415 416
 * @table: the hash table
 * @name: the name of the userdata
 *
417
 * Find the userdata specified by @name
D
Daniel Veillard 已提交
418
 *
419
 * Returns a pointer to the userdata
D
Daniel Veillard 已提交
420 421
 */
void *
E
Eric Blake 已提交
422
virHashLookup(const virHashTable *table, const void *name)
423
{
424
    size_t key;
425
    virHashEntryPtr entry;
D
Daniel Veillard 已提交
426

427 428 429
    if (!table || !name)
        return NULL;

430
    key = virHashComputeKey(table, name);
431
    for (entry = table->table[key]; entry; entry = entry->next) {
432
        if (table->keyEqual(entry->name, name))
433
            return entry->payload;
D
Daniel Veillard 已提交
434
    }
435
    return NULL;
D
Daniel Veillard 已提交
436 437
}

438 439 440 441 442 443 444 445 446

/**
 * 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.
 *
447
 * Returns a pointer to the userdata
448
 */
449
void *virHashSteal(virHashTablePtr table, const void *name)
450 451 452 453 454 455 456 457 458 459 460
{
    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 已提交
461 462 463 464 465 466 467 468 469 470 471 472 473
void *
virHashAtomicSteal(virHashAtomicPtr table,
                   const void *name)
{
    void *data;

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

    return data;
}

474

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

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


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

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

530 531 532 533 534 535 536 537 538 539 540
    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 已提交
541
        }
542
        nextptr = &entry->next;
D
Daniel Veillard 已提交
543
    }
544 545

    return -1;
D
Daniel Veillard 已提交
546
}
547

548 549 550 551 552 553 554 555

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

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

572
    for (i = 0; i < table->size; i++) {
573
        virHashEntryPtr entry = table->table[i];
574
        while (entry) {
575
            virHashEntryPtr next = entry->next;
576
            ret = iter(entry->payload, entry->name, data);
577

578 579 580
            if (ret < 0)
                goto cleanup;

581
            entry = next;
582 583
        }
    }
584

585 586 587
    ret = 0;
 cleanup:
    return ret;
588 589
}

590

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

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

614
    for (i = 0; i < table->size; i++) {
615
        virHashEntryPtr *nextptr = table->table + i;
616

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

634
    return count;
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
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);
}

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

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

685
    if (table == NULL || iter == NULL)
686
        return NULL;
687

688
    for (i = 0; i < table->size; i++) {
689 690 691
        virHashEntryPtr entry;
        for (entry = table->table[i]; entry; entry = entry->next) {
            if (iter(entry->payload, entry->name, data)) {
692 693
                if (name)
                    *name = table->keyCopy(entry->name);
694
                return entry->payload;
695 696 697
            }
        }
    }
698

699
    return NULL;
700
}
701 702 703 704

struct getKeysIter
{
    virHashKeyValuePair *sortArray;
705
    size_t arrayIdx;
706 707
};

708 709
static int virHashGetKeysIterator(void *payload,
                                  const void *key, void *data)
710 711 712 713 714 715 716
{
    struct getKeysIter *iter = data;

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

    iter->arrayIdx++;
717
    return 0;
718 719 720 721 722 723 724
}

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

virHashKeyValuePairPtr virHashGetItems(virHashTablePtr table,
                                       virHashKeyComparator compar)
{
725
    ssize_t numElems = virHashSize(table);
726 727 728 729 730 731 732 733
    struct getKeysIter iter = {
        .arrayIdx = 0,
        .sortArray = NULL,
    };

    if (numElems < 0)
        return NULL;

734
    if (VIR_ALLOC_N(iter.sortArray, numElems + 1))
735 736 737 738 739 740 741 742 743 744
        return NULL;

    virHashForEach(table, virHashGetKeysIterator, &iter);

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

    return iter.sortArray;
}
745 746 747 748

struct virHashEqualData
{
    bool equal;
E
Eric Blake 已提交
749
    const virHashTable *table2;
750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769
    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 已提交
770 771
bool virHashEqual(const virHashTable *table1,
                  const virHashTable *table2,
772 773 774 775 776 777 778 779 780 781 782 783 784 785 786
                  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;

787
    virHashSearch(table1, virHashEqualSearcher, &data, NULL);
788 789 790

    return data.equal;
}