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
 *
 * 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;
D
Daniel Veillard 已提交
45 46 47 48 49 50 51 52
    char *name;
    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;
D
Daniel Veillard 已提交
58 59 60
};

/*
61
 * virHashComputeKey:
D
Daniel Veillard 已提交
62 63 64
 * Calculate the hash key
 */
static unsigned long
65 66
virHashComputeKey(virHashTablePtr table, const char *name)
{
D
Daniel Veillard 已提交
67 68
    unsigned long value = 0L;
    char ch;
69

D
Daniel Veillard 已提交
70
    if (name != NULL) {
71 72 73 74 75
        value += 30 * (*name);
        while ((ch = *name++) != 0) {
            value =
                value ^ ((value << 5) + (value >> 3) + (unsigned long) ch);
        }
D
Daniel Veillard 已提交
76 77 78 79 80
    }
    return (value % table->size);
}

/**
81
 * virHashCreate:
D
Daniel Veillard 已提交
82
 * @size: the size of the hash table
83
 * @dataFree: function to call on each entry during virHashFree
D
Daniel Veillard 已提交
84
 *
85
 * Create a new virHashTablePtr.
D
Daniel Veillard 已提交
86 87 88
 *
 * Returns the newly created object, or NULL if an error occured.
 */
89
virHashTablePtr
90
virHashCreate(int size, virHashDataFree dataFree)
91
{
92
    virHashTablePtr table = NULL;
93

D
Daniel Veillard 已提交
94 95
    if (size <= 0)
        size = 256;
96

97 98
    if (VIR_ALLOC(table) < 0) {
        virReportOOMError();
99
        return NULL;
100
    }
101 102 103

    table->size = size;
    table->nbElems = 0;
104
    table->dataFree = dataFree;
105
    if (VIR_ALLOC_N(table->table, size) < 0) {
106
        virReportOOMError();
107 108
        VIR_FREE(table);
        return NULL;
D
Daniel Veillard 已提交
109
    }
110 111

    return table;
D
Daniel Veillard 已提交
112 113 114
}

/**
115
 * virHashGrow:
D
Daniel Veillard 已提交
116 117 118 119 120 121 122 123
 * @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
124 125
virHashGrow(virHashTablePtr table, int size)
{
D
Daniel Veillard 已提交
126 127
    unsigned long key;
    int oldsize, i;
128 129
    virHashEntryPtr iter, next;
    struct _virHashEntry *oldtable;
130

D
Daniel Veillard 已提交
131 132 133
#ifdef DEBUG_GROW
    unsigned long nbElem = 0;
#endif
134

D
Daniel Veillard 已提交
135
    if (table == NULL)
136
        return (-1);
D
Daniel Veillard 已提交
137
    if (size < 8)
138
        return (-1);
D
Daniel Veillard 已提交
139
    if (size > 8 * 2048)
140
        return (-1);
D
Daniel Veillard 已提交
141 142 143 144

    oldsize = table->size;
    oldtable = table->table;
    if (oldtable == NULL)
145 146
        return (-1);

147
    if (VIR_ALLOC_N(table->table, size) < 0) {
148
        virReportOOMError();
149 150
        table->table = oldtable;
        return (-1);
D
Daniel Veillard 已提交
151 152 153
    }
    table->size = size;

154
    /* If the two loops are merged, there would be situations where
155
     * a new entry needs to allocated and data copied into it from
156 157 158 159
     * 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 已提交
160
    for (i = 0; i < oldsize; i++) {
161 162 163 164 165
        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 已提交
166 167 168
    }

    for (i = 0; i < oldsize; i++) {
169 170 171 172 173 174 175 176 177 178 179 180
        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;
181
                VIR_FREE(iter);
182 183 184 185
            } else {
                iter->next = table->table[key].next;
                table->table[key].next = iter;
            }
D
Daniel Veillard 已提交
186 187

#ifdef DEBUG_GROW
188
            nbElem++;
D
Daniel Veillard 已提交
189 190
#endif

191 192
            iter = next;
        }
D
Daniel Veillard 已提交
193 194
    }

195
    VIR_FREE(oldtable);
D
Daniel Veillard 已提交
196 197

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

202
    return (0);
D
Daniel Veillard 已提交
203 204 205
}

/**
206
 * virHashFree:
D
Daniel Veillard 已提交
207 208 209
 * @table: the hash table
 *
 * Free the hash @table and its contents. The userdata is
210
 * deallocated with function provided at creation time.
D
Daniel Veillard 已提交
211 212
 */
void
213
virHashFree(virHashTablePtr table)
214
{
D
Daniel Veillard 已提交
215
    int i;
216 217
    virHashEntryPtr iter;
    virHashEntryPtr next;
D
Daniel Veillard 已提交
218 219 220 221
    int inside_table = 0;
    int nbElems;

    if (table == NULL)
222
        return;
D
Daniel Veillard 已提交
223
    if (table->table) {
224 225 226 227 228 229 230 231
        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;
232 233
                if ((table->dataFree != NULL) && (iter->payload != NULL))
                    table->dataFree(iter->payload, iter->name);
234
                VIR_FREE(iter->name);
235 236
                iter->payload = NULL;
                if (!inside_table)
237
                    VIR_FREE(iter);
238 239 240 241 242
                nbElems--;
                inside_table = 0;
                iter = next;
            }
        }
243
        VIR_FREE(table->table);
D
Daniel Veillard 已提交
244
    }
245
    VIR_FREE(table);
D
Daniel Veillard 已提交
246 247
}

248
static int
249 250
virHashAddOrUpdateEntry(virHashTablePtr table, const void *name,
                        void *userdata,
251
                        bool is_update)
252
{
D
Daniel Veillard 已提交
253
    unsigned long key, len = 0;
254 255
    virHashEntryPtr entry;
    virHashEntryPtr insert;
256
    char *new_name;
257
    bool found;
D
Daniel Veillard 已提交
258 259

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

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

    if (found) {
        if (is_update) {
284 285
            if (table->dataFree)
                table->dataFree(insert->payload, insert->name);
286 287 288
            insert->payload = userdata;
            return (0);
        } else {
289
            return (-1);
290
        }
D
Daniel Veillard 已提交
291 292 293
    }

    if (insert == NULL) {
294
        entry = &(table->table[key]);
D
Daniel Veillard 已提交
295
    } else {
296 297
        if (VIR_ALLOC(entry) < 0) {
            virReportOOMError();
298
            return (-1);
299
        }
D
Daniel Veillard 已提交
300 301
    }

302 303
    new_name = strdup(name);
    if (new_name == NULL) {
304
        virReportOOMError();
305 306 307 308 309
        if (insert != NULL)
            VIR_FREE(entry);
        return (-1);
    }
    entry->name = new_name;
D
Daniel Veillard 已提交
310 311 312 313
    entry->payload = userdata;
    entry->next = NULL;
    entry->valid = 1;

314 315
    if (insert != NULL)
        insert->next = entry;
D
Daniel Veillard 已提交
316 317 318 319

    table->nbElems++;

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

322
    return (0);
D
Daniel Veillard 已提交
323 324
}

325 326 327 328 329 330 331 332 333 334 335 336 337 338
/**
 * 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
virHashAddEntry(virHashTablePtr table, const char *name, void *userdata)
{
339
    return virHashAddOrUpdateEntry(table, name, userdata, false);
340 341
}

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

/**
362
 * virHashLookup:
D
Daniel Veillard 已提交
363 364 365 366 367 368 369 370
 * @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 *
371 372
virHashLookup(virHashTablePtr table, const char *name)
{
D
Daniel Veillard 已提交
373
    unsigned long key;
374
    virHashEntryPtr entry;
D
Daniel Veillard 已提交
375 376

    if (table == NULL)
377
        return (NULL);
D
Daniel Veillard 已提交
378
    if (name == NULL)
379
        return (NULL);
380
    key = virHashComputeKey(table, name);
D
Daniel Veillard 已提交
381
    if (table->table[key].valid == 0)
382
        return (NULL);
D
Daniel Veillard 已提交
383
    for (entry = &(table->table[key]); entry != NULL; entry = entry->next) {
384
        if (STREQ(entry->name, name))
385
            return (entry->payload);
D
Daniel Veillard 已提交
386
    }
387
    return (NULL);
D
Daniel Veillard 已提交
388 389
}

390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413

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

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

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

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

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

/**
 * 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
 */
499
int virHashForEach(virHashTablePtr table, virHashIterator iter, void *data) {
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
    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
 */
532
int virHashRemoveSet(virHashTablePtr table, virHashSearcher iter, const void *data) {
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 prev = NULL;
        virHashEntryPtr entry = &(table->table[i]);

        while (entry && entry->valid) {
            if (iter(entry->payload, entry->name, data)) {
                count++;
545 546
                if (table->dataFree)
                    table->dataFree(entry->payload, entry->name);
547
                VIR_FREE(entry->name);
D
Daniel Veillard 已提交
548
                table->nbElems--;
549 550
                if (prev) {
                    prev->next = entry->next;
551
                    VIR_FREE(entry);
D
Daniel Veillard 已提交
552
                    entry = prev;
553 554 555 556 557 558 559 560
                } else {
                    if (entry->next == NULL) {
                        entry->valid = 0;
                        entry->name = NULL;
                    } else {
                        entry = entry->next;
                        memcpy(&(table->table[i]), entry,
                               sizeof(virHashEntry));
561
                        VIR_FREE(entry);
D
Daniel Veillard 已提交
562 563
                        entry = &(table->table[i]);
                        continue;
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 603 604
                    }
                }
            }
            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);
}