hash.c 15.3 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;
256
    char *new_name;
D
Daniel Veillard 已提交
257 258

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

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

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

285 286 287 288 289 290 291
    new_name = strdup(name);
    if (new_name == NULL) {
        if (insert != NULL)
            VIR_FREE(entry);
        return (-1);
    }
    entry->name = new_name;
D
Daniel Veillard 已提交
292 293 294 295
    entry->payload = userdata;
    entry->next = NULL;
    entry->valid = 1;

296 297
    if (insert != NULL)
        insert->next = entry;
D
Daniel Veillard 已提交
298 299 300 301

    table->nbElems++;

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

304
    return (0);
D
Daniel Veillard 已提交
305 306 307
}

/**
308
 * virHashUpdateEntry:
D
Daniel Veillard 已提交
309 310 311 312 313 314 315 316 317 318 319 320
 * @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
321
virHashUpdateEntry(virHashTablePtr table, const char *name,
322 323
                   void *userdata, virHashDeallocator f)
{
D
Daniel Veillard 已提交
324
    unsigned long key;
325 326
    virHashEntryPtr entry;
    virHashEntryPtr insert;
327
    char *new_name;
D
Daniel Veillard 已提交
328 329

    if ((table == NULL) || name == NULL)
330
        return (-1);
D
Daniel Veillard 已提交
331 332 333 334

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

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

363 364 365 366 367 368 369
    new_name= strdup(name);
    if (new_name == NULL) {
        if (insert != NULL)
            VIR_FREE(entry);
        return (-1);
    }
    entry->name = new_name;
D
Daniel Veillard 已提交
370 371 372 373 374 375 376
    entry->payload = userdata;
    entry->next = NULL;
    entry->valid = 1;
    table->nbElems++;


    if (insert != NULL) {
377
        insert->next = entry;
D
Daniel Veillard 已提交
378
    }
379
    return (0);
D
Daniel Veillard 已提交
380 381 382
}

/**
383
 * virHashLookup:
D
Daniel Veillard 已提交
384 385 386 387 388 389 390 391
 * @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 *
392 393
virHashLookup(virHashTablePtr table, const char *name)
{
D
Daniel Veillard 已提交
394
    unsigned long key;
395
    virHashEntryPtr entry;
D
Daniel Veillard 已提交
396 397

    if (table == NULL)
398
        return (NULL);
D
Daniel Veillard 已提交
399
    if (name == NULL)
400
        return (NULL);
401
    key = virHashComputeKey(table, name);
D
Daniel Veillard 已提交
402
    if (table->table[key].valid == 0)
403
        return (NULL);
D
Daniel Veillard 已提交
404
    for (entry = &(table->table[key]); entry != NULL; entry = entry->next) {
405
        if (STREQ(entry->name, name))
406
            return (entry->payload);
D
Daniel Veillard 已提交
407
    }
408
    return (NULL);
D
Daniel Veillard 已提交
409 410 411
}

/**
412
 * virHashSize:
D
Daniel Veillard 已提交
413 414 415 416 417 418 419 420
 * @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
421 422
virHashSize(virHashTablePtr table)
{
D
Daniel Veillard 已提交
423
    if (table == NULL)
424 425
        return (-1);
    return (table->nbElems);
D
Daniel Veillard 已提交
426 427 428
}

/**
429
 * virHashRemoveEntry:
D
Daniel Veillard 已提交
430 431 432 433 434 435 436 437 438 439 440
 * @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
441
virHashRemoveEntry(virHashTablePtr table, const char *name,
442 443
                   virHashDeallocator f)
{
D
Daniel Veillard 已提交
444
    unsigned long key;
445 446
    virHashEntryPtr entry;
    virHashEntryPtr prev = NULL;
D
Daniel Veillard 已提交
447 448

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

451
    key = virHashComputeKey(table, name);
D
Daniel Veillard 已提交
452
    if (table->table[key].valid == 0) {
453
        return (-1);
D
Daniel Veillard 已提交
454
    } else {
455 456
        for (entry = &(table->table[key]); entry != NULL;
             entry = entry->next) {
457
            if (STREQ(entry->name, name)) {
D
Daniel Veillard 已提交
458 459 460
                if ((f != NULL) && (entry->payload != NULL))
                    f(entry->payload, entry->name);
                entry->payload = NULL;
461
                VIR_FREE(entry->name);
462
                if (prev) {
D
Daniel Veillard 已提交
463
                    prev->next = entry->next;
464
                    VIR_FREE(entry);
465 466 467 468 469 470 471
                } else {
                    if (entry->next == NULL) {
                        entry->valid = 0;
                    } else {
                        entry = entry->next;
                        memcpy(&(table->table[key]), entry,
                               sizeof(virHashEntry));
472
                        VIR_FREE(entry);
473 474
                    }
                }
D
Daniel Veillard 已提交
475
                table->nbElems--;
476
                return (0);
D
Daniel Veillard 已提交
477 478 479
            }
            prev = entry;
        }
480
        return (-1);
D
Daniel Veillard 已提交
481 482
    }
}
483

484 485 486 487 488 489 490 491 492 493 494 495 496 497

/**
 * 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
 */
498
int virHashForEach(virHashTablePtr table, virHashIterator iter, void *data) {
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 532 533 534 535 536 537 538 539 540 541 542 543 544
    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);
545
                VIR_FREE(entry->name);
D
Daniel Veillard 已提交
546
                table->nbElems--;
547 548
                if (prev) {
                    prev->next = entry->next;
549
                    VIR_FREE(entry);
D
Daniel Veillard 已提交
550
                    entry = prev;
551 552 553 554 555 556 557 558
                } else {
                    if (entry->next == NULL) {
                        entry->valid = 0;
                        entry->name = NULL;
                    } else {
                        entry = entry->next;
                        memcpy(&(table->table[i]), entry,
                               sizeof(virHashEntry));
559
                        VIR_FREE(entry);
D
Daniel Veillard 已提交
560 561
                        entry = &(table->table[i]);
                        continue;
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 590 591 592 593 594 595 596 597 598 599 600 601 602
                    }
                }
            }
            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);
}