Coverage Report

Created: 2025-07-18 06:31

/src/libxml2/hash.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * hash.c: hash tables
3
 *
4
 * Hash table with open addressing, linear probing and
5
 * Robin Hood reordering.
6
 *
7
 * See Copyright for the status of this software.
8
 */
9
10
#define IN_LIBXML
11
#include "libxml.h"
12
13
#include <string.h>
14
#include <limits.h>
15
16
#include <libxml/parser.h>
17
#include <libxml/hash.h>
18
#include <libxml/dict.h>
19
#include <libxml/xmlmemory.h>
20
#include <libxml/xmlstring.h>
21
22
#include "private/dict.h"
23
24
#ifndef SIZE_MAX
25
  #define SIZE_MAX ((size_t) -1)
26
#endif
27
28
1.02M
#define MAX_FILL_NUM 7
29
1.02M
#define MAX_FILL_DENOM 8
30
704k
#define MIN_HASH_SIZE 8
31
5.90M
#define MAX_HASH_SIZE (1u << 31)
32
33
/*
34
 * A single entry in the hash table
35
 */
36
typedef struct {
37
    unsigned hashValue; /* 0 means unoccupied, occupied entries have the
38
                         * MAX_HASH_SIZE bit set to 1 */
39
    xmlChar *key;
40
    xmlChar *key2; /* TODO: Don't allocate possibly empty keys */
41
    xmlChar *key3;
42
    void *payload;
43
} xmlHashEntry;
44
45
/*
46
 * The entire hash table
47
 */
48
struct _xmlHashTable {
49
    xmlHashEntry *table;
50
    unsigned size; /* power of two */
51
    unsigned nbElems;
52
    xmlDictPtr dict;
53
    unsigned randomSeed;
54
};
55
56
static int
57
xmlHashGrow(xmlHashTablePtr hash, unsigned size);
58
59
ATTRIBUTE_NO_SANITIZE_INTEGER
60
static unsigned
61
xmlHashValue(unsigned seed, const xmlChar *key, const xmlChar *key2,
62
6.34M
             const xmlChar *key3, size_t *lengths) {
63
6.34M
    unsigned h1, h2;
64
6.34M
    size_t i;
65
66
6.34M
    HASH_INIT(h1, h2, seed);
67
68
898M
    for (i = 0; key[i] != 0; i++) {
69
892M
        HASH_UPDATE(h1, h2, key[i]);
70
892M
    }
71
6.34M
    if (lengths)
72
1.27M
        lengths[0] = i;
73
74
6.34M
    HASH_UPDATE(h1, h2, 0);
75
76
6.34M
    if (key2 != NULL) {
77
29.9M
        for (i = 0; key2[i] != 0; i++) {
78
28.8M
            HASH_UPDATE(h1, h2, key2[i]);
79
28.8M
        }
80
1.11M
        if (lengths)
81
545k
            lengths[1] = i;
82
1.11M
    }
83
84
6.34M
    HASH_UPDATE(h1, h2, 0);
85
86
6.34M
    if (key3 != NULL) {
87
603k
        for (i = 0; key3[i] != 0; i++) {
88
495k
            HASH_UPDATE(h1, h2, key3[i]);
89
495k
        }
90
108k
        if (lengths)
91
51.1k
            lengths[2] = i;
92
108k
    }
93
94
6.34M
    HASH_FINISH(h1, h2);
95
96
6.34M
    return(h2);
97
6.34M
}
98
99
ATTRIBUTE_NO_SANITIZE_INTEGER
100
static unsigned
101
xmlHashQNameValue(unsigned seed,
102
                  const xmlChar *prefix, const xmlChar *name,
103
                  const xmlChar *prefix2, const xmlChar *name2,
104
53.9k
                  const xmlChar *prefix3, const xmlChar *name3) {
105
53.9k
    unsigned h1, h2, ch;
106
107
53.9k
    HASH_INIT(h1, h2, seed);
108
109
53.9k
    if (prefix != NULL) {
110
65.2k
        while ((ch = *prefix++) != 0) {
111
46.3k
            HASH_UPDATE(h1, h2, ch);
112
46.3k
        }
113
18.9k
        HASH_UPDATE(h1, h2, ':');
114
18.9k
    }
115
53.9k
    if (name != NULL) {
116
307k
        while ((ch = *name++) != 0) {
117
253k
            HASH_UPDATE(h1, h2, ch);
118
253k
        }
119
53.9k
    }
120
53.9k
    HASH_UPDATE(h1, h2, 0);
121
53.9k
    if (prefix2 != NULL) {
122
24.3k
        while ((ch = *prefix2++) != 0) {
123
20.1k
            HASH_UPDATE(h1, h2, ch);
124
20.1k
        }
125
4.17k
        HASH_UPDATE(h1, h2, ':');
126
4.17k
    }
127
53.9k
    if (name2 != NULL) {
128
278k
        while ((ch = *name2++) != 0) {
129
224k
            HASH_UPDATE(h1, h2, ch);
130
224k
        }
131
53.9k
    }
132
53.9k
    HASH_UPDATE(h1, h2, 0);
133
53.9k
    if (prefix3 != NULL) {
134
0
        while ((ch = *prefix3++) != 0) {
135
0
            HASH_UPDATE(h1, h2, ch);
136
0
        }
137
0
        HASH_UPDATE(h1, h2, ':');
138
0
    }
139
53.9k
    if (name3 != NULL) {
140
0
        while ((ch = *name3++) != 0) {
141
0
            HASH_UPDATE(h1, h2, ch);
142
0
        }
143
0
    }
144
145
53.9k
    HASH_FINISH(h1, h2);
146
147
53.9k
    return(h2);
148
53.9k
}
149
150
/**
151
 * xmlHashCreate:
152
 * @size: initial size of the hash table
153
 *
154
 * Create a new hash table. Set size to zero if the number of entries
155
 * can't be estimated.
156
 *
157
 * Returns the newly created object, or NULL if a memory allocation failed.
158
 */
159
xmlHashTablePtr
160
356k
xmlHashCreate(int size) {
161
356k
    xmlHashTablePtr hash;
162
163
356k
    xmlInitParser();
164
165
356k
    hash = xmlMalloc(sizeof(*hash));
166
356k
    if (hash == NULL)
167
3.18k
        return(NULL);
168
353k
    hash->dict = NULL;
169
353k
    hash->size = 0;
170
353k
    hash->table = NULL;
171
353k
    hash->nbElems = 0;
172
353k
    hash->randomSeed = xmlRandom();
173
353k
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
174
353k
    hash->randomSeed = 0;
175
353k
#endif
176
177
    /*
178
     * Unless a larger size is passed, the backing table is created
179
     * lazily with MIN_HASH_SIZE capacity. In practice, there are many
180
     * hash tables which are never filled.
181
     */
182
353k
    if (size > MIN_HASH_SIZE) {
183
162k
        unsigned newSize = MIN_HASH_SIZE * 2;
184
185
439k
        while ((newSize < (unsigned) size) && (newSize < MAX_HASH_SIZE))
186
276k
            newSize *= 2;
187
188
162k
        if (xmlHashGrow(hash, newSize) != 0) {
189
26
            xmlFree(hash);
190
26
            return(NULL);
191
26
        }
192
162k
    }
193
194
353k
    return(hash);
195
353k
}
196
197
/**
198
 * xmlHashCreateDict:
199
 * @size: the size of the hash table
200
 * @dict: a dictionary to use for the hash
201
 *
202
 * Create a new hash table backed by a dictionary. This can reduce
203
 * resource usage considerably if most keys passed to API functions
204
 * originate from this dictionary.
205
 *
206
 * Returns the newly created object, or NULL if a memory allocation failed.
207
 */
208
xmlHashTablePtr
209
161k
xmlHashCreateDict(int size, xmlDictPtr dict) {
210
161k
    xmlHashTablePtr hash;
211
212
161k
    hash = xmlHashCreate(size);
213
161k
    if (hash != NULL) {
214
161k
        hash->dict = dict;
215
161k
        xmlDictReference(dict);
216
161k
    }
217
161k
    return(hash);
218
161k
}
219
220
/**
221
 * xmlHashFree:
222
 * @hash: hash table
223
 * @dealloc: deallocator function or NULL
224
 *
225
 * Free the hash and its contents. The payload is deallocated with
226
 * @dealloc if provided.
227
 */
228
void
229
578k
xmlHashFree(xmlHashTablePtr hash, xmlHashDeallocator dealloc) {
230
578k
    if (hash == NULL)
231
224k
        return;
232
233
353k
    if (hash->table) {
234
350k
        const xmlHashEntry *end = &hash->table[hash->size];
235
350k
        const xmlHashEntry *entry;
236
237
48.8M
        for (entry = hash->table; entry < end; entry++) {
238
48.5M
            if (entry->hashValue == 0)
239
47.3M
                continue;
240
1.17M
            if ((dealloc != NULL) && (entry->payload != NULL))
241
453k
                dealloc(entry->payload, entry->key);
242
1.17M
            if (hash->dict == NULL) {
243
879k
                if (entry->key)
244
879k
                    xmlFree(entry->key);
245
879k
                if (entry->key2)
246
430k
                    xmlFree(entry->key2);
247
879k
                if (entry->key3)
248
26
                    xmlFree(entry->key3);
249
879k
            }
250
1.17M
        }
251
252
350k
        xmlFree(hash->table);
253
350k
    }
254
255
353k
    if (hash->dict)
256
146k
        xmlDictFree(hash->dict);
257
258
353k
    xmlFree(hash);
259
353k
}
260
261
/**
262
 * xmlFastStrEqual:
263
 * @s1: string
264
 * @s2: string
265
 *
266
 * Compare two strings for equality, allowing NULL values.
267
 */
268
static int
269
5.19M
xmlFastStrEqual(const xmlChar *s1, const xmlChar *s2) {
270
5.19M
    if (s1 == NULL)
271
4.95M
        return(s2 == NULL);
272
231k
    else
273
231k
        return((s2 != NULL) &&
274
231k
               (strcmp((const char *) s1, (const char *) s2) == 0));
275
5.19M
}
276
277
/**
278
 * xmlHashFindEntry:
279
 * @hash: hash table, non-NULL, size > 0
280
 * @key: first string key, non-NULL
281
 * @key2: second string key
282
 * @key3: third string key
283
 * @hashValue: valid hash value of keys
284
 * @pfound: result of search
285
 *
286
 * Try to find a matching hash table entry. If an entry was found, set
287
 * @found to 1 and return the entry. Otherwise, set @found to 0 and return
288
 * the location where a new entry should be inserted.
289
 */
290
ATTRIBUTE_NO_SANITIZE_INTEGER
291
static xmlHashEntry *
292
xmlHashFindEntry(const xmlHashTable *hash, const xmlChar *key,
293
                 const xmlChar *key2, const xmlChar *key3,
294
6.15M
                 unsigned hashValue, int *pfound) {
295
6.15M
    xmlHashEntry *entry;
296
6.15M
    unsigned mask, pos, displ;
297
6.15M
    int found = 0;
298
299
6.15M
    mask = hash->size - 1;
300
6.15M
    pos = hashValue & mask;
301
6.15M
    entry = &hash->table[pos];
302
303
6.15M
    if (entry->hashValue != 0) {
304
        /*
305
         * Robin hood hashing: abort if the displacement of the entry
306
         * is smaller than the displacement of the key we look for.
307
         * This also stops at the correct position when inserting.
308
         */
309
4.34M
        displ = 0;
310
4.34M
        hashValue |= MAX_HASH_SIZE;
311
312
5.29M
        do {
313
5.29M
            if (entry->hashValue == hashValue) {
314
3.23M
                if (hash->dict) {
315
772k
                    if ((entry->key == key) &&
316
772k
                        (entry->key2 == key2) &&
317
772k
                        (entry->key3 == key3)) {
318
638k
                        found = 1;
319
638k
                        break;
320
638k
                    }
321
772k
                }
322
2.59M
                if ((strcmp((const char *) entry->key,
323
2.59M
                            (const char *) key) == 0) &&
324
2.59M
                    (xmlFastStrEqual(entry->key2, key2)) &&
325
2.59M
                    (xmlFastStrEqual(entry->key3, key3))) {
326
2.59M
                    found = 1;
327
2.59M
                    break;
328
2.59M
                }
329
2.59M
            }
330
331
2.06M
            displ++;
332
2.06M
            pos++;
333
2.06M
            entry++;
334
2.06M
            if ((pos & mask) == 0)
335
105k
                entry = hash->table;
336
2.06M
        } while ((entry->hashValue != 0) &&
337
2.06M
                 (((pos - entry->hashValue) & mask) >= displ));
338
4.34M
    }
339
340
0
    *pfound = found;
341
6.15M
    return(entry);
342
6.15M
}
343
344
/**
345
 * xmlHashGrow:
346
 * @hash: hash table
347
 * @size: new size of the hash table
348
 *
349
 * Resize the hash table.
350
 *
351
 * Returns 0 in case of success, -1 if a memory allocation failed.
352
 */
353
static int
354
395k
xmlHashGrow(xmlHashTablePtr hash, unsigned size) {
355
395k
    const xmlHashEntry *oldentry, *oldend, *end;
356
395k
    xmlHashEntry *table;
357
395k
    unsigned oldsize, i;
358
359
    /* Add 0 to avoid spurious -Wtype-limits warning on 64-bit GCC */
360
395k
    if ((size_t) size + 0 > SIZE_MAX / sizeof(table[0]))
361
0
        return(-1);
362
395k
    table = xmlMalloc(size * sizeof(table[0]));
363
395k
    if (table == NULL)
364
81
        return(-1);
365
395k
    memset(table, 0, size * sizeof(table[0]));
366
367
395k
    oldsize = hash->size;
368
395k
    if (oldsize == 0)
369
350k
        goto done;
370
371
45.3k
    oldend = &hash->table[oldsize];
372
45.3k
    end = &table[size];
373
374
    /*
375
     * Robin Hood sorting order is maintained if we
376
     *
377
     * - compute hash indices with modulo
378
     * - resize by an integer factor
379
     * - start to copy from the beginning of a probe sequence
380
     */
381
45.3k
    oldentry = hash->table;
382
218k
    while (oldentry->hashValue != 0) {
383
172k
        if (++oldentry >= oldend)
384
0
            oldentry = hash->table;
385
172k
    }
386
387
623k
    for (i = 0; i < oldsize; i++) {
388
578k
        if (oldentry->hashValue != 0) {
389
505k
            xmlHashEntry *entry = &table[oldentry->hashValue & (size - 1)];
390
391
712k
            while (entry->hashValue != 0) {
392
206k
                if (++entry >= end)
393
20.6k
                    entry = table;
394
206k
            }
395
505k
            *entry = *oldentry;
396
505k
        }
397
398
578k
        if (++oldentry >= oldend)
399
45.3k
            oldentry = hash->table;
400
578k
    }
401
402
45.3k
    xmlFree(hash->table);
403
404
395k
done:
405
395k
    hash->table = table;
406
395k
    hash->size = size;
407
408
395k
    return(0);
409
45.3k
}
410
411
/**
412
 * xmlHashUpdateInternal:
413
 * @hash: hash table
414
 * @key: first string key
415
 * @key2: second string key
416
 * @key3: third string key
417
 * @payload: pointer to the payload
418
 * @dealloc: deallocator function for replaced item or NULL
419
 * @update: whether existing entries should be updated
420
 *
421
 * Internal function to add or update hash entries.
422
 */
423
ATTRIBUTE_NO_SANITIZE_INTEGER
424
static int
425
xmlHashUpdateInternal(xmlHashTablePtr hash, const xmlChar *key,
426
                      const xmlChar *key2, const xmlChar *key3,
427
1.28M
                      void *payload, xmlHashDeallocator dealloc, int update) {
428
1.28M
    xmlChar *copy, *copy2, *copy3;
429
1.28M
    xmlHashEntry *entry = NULL;
430
1.28M
    size_t lengths[3] = {0, 0, 0};
431
1.28M
    unsigned hashValue, newSize;
432
433
1.28M
    if ((hash == NULL) || (key == NULL))
434
11.5k
        return(-1);
435
436
1.27M
    hashValue = xmlHashValue(hash->randomSeed, key, key2, key3, lengths);
437
438
    /*
439
     * Check for an existing entry
440
     */
441
1.27M
    if (hash->size == 0) {
442
187k
        newSize = MIN_HASH_SIZE;
443
1.08M
    } else {
444
1.08M
        int found = 0;
445
446
1.08M
        entry = xmlHashFindEntry(hash, key, key2, key3, hashValue, &found);
447
448
1.08M
        if (found) {
449
61.8k
            if (update) {
450
21.6k
                if (dealloc)
451
478
                    dealloc(entry->payload, entry->key);
452
21.6k
                entry->payload = payload;
453
21.6k
            }
454
455
61.8k
            return(0);
456
61.8k
        }
457
458
1.02M
        if (hash->nbElems + 1 > hash->size / MAX_FILL_DENOM * MAX_FILL_NUM) {
459
            /* This guarantees that nbElems < INT_MAX */
460
45.3k
            if (hash->size >= MAX_HASH_SIZE)
461
0
                return(-1);
462
45.3k
            newSize = hash->size * 2;
463
975k
        } else {
464
975k
            newSize = 0;
465
975k
        }
466
1.02M
    }
467
468
    /*
469
     * Grow the hash table if needed
470
     */
471
1.20M
    if (newSize > 0) {
472
232k
        unsigned mask, displ, pos;
473
474
232k
        if (xmlHashGrow(hash, newSize) != 0)
475
55
            return(-1);
476
477
        /*
478
         * Find new entry
479
         */
480
232k
        mask = hash->size - 1;
481
232k
        displ = 0;
482
232k
        pos = hashValue & mask;
483
232k
        entry = &hash->table[pos];
484
485
232k
        if (entry->hashValue != 0) {
486
25.3k
            do {
487
25.3k
                displ++;
488
25.3k
                pos++;
489
25.3k
                entry++;
490
25.3k
                if ((pos & mask) == 0)
491
1.18k
                    entry = hash->table;
492
25.3k
            } while ((entry->hashValue != 0) &&
493
25.3k
                     ((pos - entry->hashValue) & mask) >= displ);
494
22.9k
        }
495
232k
    }
496
497
    /*
498
     * Copy keys
499
     */
500
1.20M
    if (hash->dict != NULL) {
501
325k
        if (xmlDictOwns(hash->dict, key)) {
502
321k
            copy = (xmlChar *) key;
503
321k
        } else {
504
4.09k
            copy = (xmlChar *) xmlDictLookup(hash->dict, key, -1);
505
4.09k
            if (copy == NULL)
506
0
                return(-1);
507
4.09k
        }
508
509
325k
        if ((key2 == NULL) || (xmlDictOwns(hash->dict, key2))) {
510
324k
            copy2 = (xmlChar *) key2;
511
324k
        } else {
512
1.59k
            copy2 = (xmlChar *) xmlDictLookup(hash->dict, key2, -1);
513
1.59k
            if (copy2 == NULL)
514
0
                return(-1);
515
1.59k
        }
516
325k
        if ((key3 == NULL) || (xmlDictOwns(hash->dict, key3))) {
517
325k
            copy3 = (xmlChar *) key3;
518
325k
        } else {
519
0
            copy3 = (xmlChar *) xmlDictLookup(hash->dict, key3, -1);
520
0
            if (copy3 == NULL)
521
0
                return(-1);
522
0
        }
523
882k
    } else {
524
882k
        copy = xmlMalloc(lengths[0] + 1);
525
882k
        if (copy == NULL)
526
853
            return(-1);
527
881k
        memcpy(copy, key, lengths[0] + 1);
528
529
881k
        if (key2 != NULL) {
530
430k
            copy2 = xmlMalloc(lengths[1] + 1);
531
430k
            if (copy2 == NULL) {
532
27
                xmlFree(copy);
533
27
                return(-1);
534
27
            }
535
430k
            memcpy(copy2, key2, lengths[1] + 1);
536
451k
        } else {
537
451k
            copy2 = NULL;
538
451k
        }
539
540
881k
        if (key3 != NULL) {
541
27
            copy3 = xmlMalloc(lengths[2] + 1);
542
27
            if (copy3 == NULL) {
543
1
                xmlFree(copy);
544
1
                xmlFree(copy2);
545
1
                return(-1);
546
1
            }
547
26
            memcpy(copy3, key3, lengths[2] + 1);
548
881k
        } else {
549
881k
            copy3 = NULL;
550
881k
        }
551
881k
    }
552
553
    /*
554
     * Shift the remainder of the probe sequence to the right
555
     */
556
1.20M
    if (entry->hashValue != 0) {
557
222k
        const xmlHashEntry *end = &hash->table[hash->size];
558
222k
        const xmlHashEntry *cur = entry;
559
560
467k
        do {
561
467k
            cur++;
562
467k
            if (cur >= end)
563
59.5k
                cur = hash->table;
564
467k
        } while (cur->hashValue != 0);
565
566
222k
        if (cur < entry) {
567
            /*
568
             * If we traversed the end of the buffer, handle the part
569
             * at the start of the buffer.
570
             */
571
59.5k
            memmove(&hash->table[1], hash->table,
572
59.5k
                    (char *) cur - (char *) hash->table);
573
59.5k
            cur = end - 1;
574
59.5k
            hash->table[0] = *cur;
575
59.5k
        }
576
577
222k
        memmove(&entry[1], entry, (char *) cur - (char *) entry);
578
222k
    }
579
580
    /*
581
     * Populate entry
582
     */
583
1.20M
    entry->key = copy;
584
1.20M
    entry->key2 = copy2;
585
1.20M
    entry->key3 = copy3;
586
1.20M
    entry->payload = payload;
587
    /* OR with MAX_HASH_SIZE to make sure that the value is non-zero */
588
1.20M
    entry->hashValue = hashValue | MAX_HASH_SIZE;
589
590
1.20M
    hash->nbElems++;
591
592
1.20M
    return(1);
593
1.20M
}
594
595
/**
596
 * xmlHashDefaultDeallocator:
597
 * @entry: hash table entry
598
 * @key: the entry's string key
599
 *
600
 * Free a hash table entry with xmlFree.
601
 */
602
void
603
132k
xmlHashDefaultDeallocator(void *entry, const xmlChar *key ATTRIBUTE_UNUSED) {
604
132k
    xmlFree(entry);
605
132k
}
606
607
/**
608
 * xmlHashAdd:
609
 * @hash: hash table
610
 * @key: string key
611
 * @payload: pointer to the payload
612
 *
613
 * Add a hash table entry. If an entry with this key already exists,
614
 * payload will not be updated and 0 is returned. This return value
615
 * can't be distinguished from out-of-memory errors, so this function
616
 * should be used with care.
617
 *
618
 * Available since 2.13.0.
619
 *
620
 * Returns 1 on success, 0 if an entry exists and -1 in case of error.
621
 */
622
int
623
152k
xmlHashAdd(xmlHashTablePtr hash, const xmlChar *key, void *payload) {
624
152k
    return(xmlHashUpdateInternal(hash, key, NULL, NULL, payload, NULL, 0));
625
152k
}
626
627
/**
628
 * xmlHashAdd2:
629
 * @hash: hash table
630
 * @key: first string key
631
 * @key2: second string key
632
 * @payload: pointer to the payload
633
 *
634
 * Add a hash table entry with two strings as key.
635
 *
636
 * See xmlHashAdd.
637
 *
638
 * Available since 2.13.0.
639
 *
640
 * Returns 1 on success, 0 if an entry exists and -1 in case of error.
641
 */
642
int
643
xmlHashAdd2(xmlHashTablePtr hash, const xmlChar *key,
644
148k
                 const xmlChar *key2, void *payload) {
645
148k
    return(xmlHashUpdateInternal(hash, key, key2, NULL, payload, NULL, 0));
646
148k
}
647
648
/**
649
 * xmlHashAdd3:
650
 * @hash: hash table
651
 * @key: first string key
652
 * @key2: second string key
653
 * @key3: third string key
654
 * @payload: pointer to the payload
655
 *
656
 * Add a hash table entry with three strings as key.
657
 *
658
 * See xmlHashAdd.
659
 *
660
 * Available since 2.13.0.
661
 *
662
 * Returns 1 on success, 0 if an entry exists and -1 in case of error.
663
 */
664
int
665
xmlHashAdd3(xmlHashTablePtr hash, const xmlChar *key,
666
                 const xmlChar *key2, const xmlChar *key3,
667
51.1k
                 void *payload) {
668
51.1k
    return(xmlHashUpdateInternal(hash, key, key2, key3, payload, NULL, 0));
669
51.1k
}
670
671
/**
672
 * xmlHashAddEntry:
673
 * @hash: hash table
674
 * @key: string key
675
 * @payload: pointer to the payload
676
 *
677
 * Add a hash table entry. If an entry with this key already exists,
678
 * payload will not be updated and -1 is returned. This return value
679
 * can't be distinguished from out-of-memory errors, so this function
680
 * should be used with care.
681
 *
682
 * NOTE: This function doesn't allow to distinguish malloc failures from
683
 *       existing entries. Use xmlHashAdd instead.
684
 *
685
 * Returns 0 on success and -1 in case of error.
686
 */
687
int
688
117k
xmlHashAddEntry(xmlHashTablePtr hash, const xmlChar *key, void *payload) {
689
117k
    int res = xmlHashUpdateInternal(hash, key, NULL, NULL, payload, NULL, 0);
690
691
117k
    if (res == 0)
692
329
        res = -1;
693
117k
    else if (res == 1)
694
117k
        res = 0;
695
696
117k
    return(res);
697
117k
}
698
699
/**
700
 * xmlHashAddEntry2:
701
 * @hash: hash table
702
 * @key: first string key
703
 * @key2: second string key
704
 * @payload: pointer to the payload
705
 *
706
 * Add a hash table entry with two strings as key.
707
 *
708
 * See xmlHashAddEntry.
709
 *
710
 * Returns 0 on success and -1 in case of error.
711
 */
712
int
713
xmlHashAddEntry2(xmlHashTablePtr hash, const xmlChar *key,
714
704k
                 const xmlChar *key2, void *payload) {
715
704k
    int res = xmlHashUpdateInternal(hash, key, key2, NULL, payload, NULL, 0);
716
717
704k
    if (res == 0)
718
11.7k
        res = -1;
719
692k
    else if (res == 1)
720
679k
        res = 0;
721
722
704k
    return(res);
723
704k
}
724
725
/**
726
 * xmlHashAddEntry3:
727
 * @hash: hash table
728
 * @key: first string key
729
 * @key2: second string key
730
 * @key3: third string key
731
 * @payload: pointer to the payload
732
 *
733
 * Add a hash table entry with three strings as key.
734
 *
735
 * See xmlHashAddEntry.
736
 *
737
 * Returns 0 on success and -1 in case of error.
738
 */
739
int
740
xmlHashAddEntry3(xmlHashTablePtr hash, const xmlChar *key,
741
                 const xmlChar *key2, const xmlChar *key3,
742
56.1k
                 void *payload) {
743
56.1k
    int res = xmlHashUpdateInternal(hash, key, key2, key3, payload, NULL, 0);
744
745
56.1k
    if (res == 0)
746
0
        res = -1;
747
56.1k
    else if (res == 1)
748
56.0k
        res = 0;
749
750
56.1k
    return(res);
751
56.1k
}
752
753
/**
754
 * xmlHashUpdateEntry:
755
 * @hash: hash table
756
 * @key: string key
757
 * @payload: pointer to the payload
758
 * @dealloc: deallocator function for replaced item or NULL
759
 *
760
 * Add a hash table entry. If an entry with this key already exists,
761
 * the old payload will be freed and updated with the new value.
762
 *
763
 * Returns 0 in case of success, -1 if a memory allocation failed.
764
 */
765
int
766
xmlHashUpdateEntry(xmlHashTablePtr hash, const xmlChar *key,
767
11.2k
                   void *payload, xmlHashDeallocator dealloc) {
768
11.2k
    int res = xmlHashUpdateInternal(hash, key, NULL, NULL, payload,
769
11.2k
                                    dealloc, 1);
770
771
11.2k
    if (res == 1)
772
10.7k
        res = 0;
773
774
11.2k
    return(res);
775
11.2k
}
776
777
/**
778
 * xmlHashUpdateEntry2:
779
 * @hash: hash table
780
 * @key: first string key
781
 * @key2: second string key
782
 * @payload: pointer to the payload
783
 * @dealloc: deallocator function for replaced item or NULL
784
 *
785
 * Add a hash table entry with two strings as key.
786
 *
787
 * See xmlHashUpdateEntry.
788
 *
789
 * Returns 0 on success and -1 in case of error.
790
 */
791
int
792
xmlHashUpdateEntry2(xmlHashTablePtr hash, const xmlChar *key,
793
                   const xmlChar *key2, void *payload,
794
25.9k
                   xmlHashDeallocator dealloc) {
795
25.9k
    int res = xmlHashUpdateInternal(hash, key, key2, NULL, payload,
796
25.9k
                                    dealloc, 1);
797
798
25.9k
    if (res == 1)
799
20.1k
        res = 0;
800
801
25.9k
    return(res);
802
25.9k
}
803
804
/**
805
 * xmlHashUpdateEntry3:
806
 * @hash: hash table
807
 * @key: first string key
808
 * @key2: second string key
809
 * @key3: third string key
810
 * @payload: pointer to the payload
811
 * @dealloc: deallocator function for replaced item or NULL
812
 *
813
 * Add a hash table entry with three strings as key.
814
 *
815
 * See xmlHashUpdateEntry.
816
 *
817
 * Returns 0 on success and -1 in case of error.
818
 */
819
int
820
xmlHashUpdateEntry3(xmlHashTablePtr hash, const xmlChar *key,
821
                   const xmlChar *key2, const xmlChar *key3,
822
15.4k
                   void *payload, xmlHashDeallocator dealloc) {
823
15.4k
    int res = xmlHashUpdateInternal(hash, key, key2, key3, payload,
824
15.4k
                                    dealloc, 1);
825
826
15.4k
    if (res == 1)
827
0
        res = 0;
828
829
15.4k
    return(res);
830
15.4k
}
831
832
/**
833
 * xmlHashLookup:
834
 * @hash: hash table
835
 * @key: string key
836
 *
837
 * Find the entry specified by @key.
838
 *
839
 * Returns a pointer to the payload or NULL if no entry was found.
840
 */
841
void *
842
3.39M
xmlHashLookup(xmlHashTablePtr hash, const xmlChar *key) {
843
3.39M
    return(xmlHashLookup3(hash, key, NULL, NULL));
844
3.39M
}
845
846
/**
847
 * xmlHashLookup2:
848
 * @hash: hash table
849
 * @key: first string key
850
 * @key2: second string key
851
 *
852
 * Find the payload specified by the (@key, @key2) tuple.
853
 *
854
 * Returns a pointer to the payload or NULL if no entry was found.
855
 */
856
void *
857
xmlHashLookup2(xmlHashTablePtr hash, const xmlChar *key,
858
1.08M
              const xmlChar *key2) {
859
1.08M
    return(xmlHashLookup3(hash, key, key2, NULL));
860
1.08M
}
861
862
/**
863
 * xmlHashQLookup:
864
 * @hash: hash table
865
 * @prefix: prefix of the string key
866
 * @name: local name of the string key
867
 *
868
 * Find the payload specified by the QName @prefix:@name or @name.
869
 *
870
 * Returns a pointer to the payload or NULL if no entry was found.
871
 */
872
void *
873
xmlHashQLookup(xmlHashTablePtr hash, const xmlChar *prefix,
874
0
               const xmlChar *name) {
875
0
    return(xmlHashQLookup3(hash, prefix, name, NULL, NULL, NULL, NULL));
876
0
}
877
878
/**
879
 * xmlHashQLookup2:
880
 * @hash: hash table
881
 * @prefix: first prefix
882
 * @name: first local name
883
 * @prefix2: second prefix
884
 * @name2: second local name
885
 *
886
 * Find the payload specified by the QNames tuple.
887
 *
888
 * Returns a pointer to the payload or NULL if no entry was found.
889
 */
890
void *
891
xmlHashQLookup2(xmlHashTablePtr hash, const xmlChar *prefix,
892
                const xmlChar *name, const xmlChar *prefix2,
893
53.9k
                const xmlChar *name2) {
894
53.9k
    return(xmlHashQLookup3(hash, prefix, name, prefix2, name2, NULL, NULL));
895
53.9k
}
896
897
/**
898
 * xmlHashLookup3:
899
 * @hash: hash table
900
 * @key: first string key
901
 * @key2: second string key
902
 * @key3: third string key
903
 *
904
 * Find the payload specified by the (@key, @key2, @key3) tuple.
905
 *
906
 * Returns a pointer to the payload or NULL if no entry was found.
907
 */
908
void *
909
xmlHashLookup3(xmlHashTablePtr hash, const xmlChar *key,
910
5.27M
               const xmlChar *key2, const xmlChar *key3) {
911
5.27M
    const xmlHashEntry *entry;
912
5.27M
    unsigned hashValue;
913
5.27M
    int found;
914
915
5.27M
    if ((hash == NULL) || (hash->size == 0) || (key == NULL))
916
233k
        return(NULL);
917
5.03M
    hashValue = xmlHashValue(hash->randomSeed, key, key2, key3, NULL);
918
5.03M
    entry = xmlHashFindEntry(hash, key, key2, key3, hashValue, &found);
919
5.03M
    if (found)
920
3.14M
        return(entry->payload);
921
1.89M
    return(NULL);
922
5.03M
}
923
924
/**
925
 * xmlHashQLookup3:
926
 * @hash: hash table
927
 * @prefix: first prefix
928
 * @name: first local name
929
 * @prefix2: second prefix
930
 * @name2: second local name
931
 * @prefix3: third prefix
932
 * @name3: third local name
933
 *
934
 * Find the payload specified by the QNames tuple.
935
 *
936
 * Returns a pointer to the payload or NULL if no entry was found.
937
 */
938
ATTRIBUTE_NO_SANITIZE_INTEGER
939
void *
940
xmlHashQLookup3(xmlHashTablePtr hash,
941
                const xmlChar *prefix, const xmlChar *name,
942
                const xmlChar *prefix2, const xmlChar *name2,
943
53.9k
                const xmlChar *prefix3, const xmlChar *name3) {
944
53.9k
    const xmlHashEntry *entry;
945
53.9k
    unsigned hashValue, mask, pos, displ;
946
947
53.9k
    if ((hash == NULL) || (hash->size == 0) || (name == NULL))
948
0
        return(NULL);
949
950
53.9k
    hashValue = xmlHashQNameValue(hash->randomSeed, prefix, name, prefix2,
951
53.9k
                                  name2, prefix3, name3);
952
53.9k
    mask = hash->size - 1;
953
53.9k
    pos = hashValue & mask;
954
53.9k
    entry = &hash->table[pos];
955
956
53.9k
    if (entry->hashValue != 0) {
957
30.3k
        displ = 0;
958
30.3k
        hashValue |= MAX_HASH_SIZE;
959
960
34.2k
        do {
961
34.2k
            if ((hashValue == entry->hashValue) &&
962
34.2k
                (xmlStrQEqual(prefix, name, entry->key)) &&
963
34.2k
                (xmlStrQEqual(prefix2, name2, entry->key2)) &&
964
34.2k
                (xmlStrQEqual(prefix3, name3, entry->key3)))
965
25.5k
                return(entry->payload);
966
967
8.75k
            displ++;
968
8.75k
            pos++;
969
8.75k
            entry++;
970
8.75k
            if ((pos & mask) == 0)
971
552
                entry = hash->table;
972
8.75k
        } while ((entry->hashValue != 0) &&
973
8.75k
                 (((pos - entry->hashValue) & mask) >= displ));
974
30.3k
    }
975
976
28.4k
    return(NULL);
977
53.9k
}
978
979
typedef struct {
980
    xmlHashScanner scan;
981
    void *data;
982
} stubData;
983
984
static void
985
stubHashScannerFull(void *payload, void *data, const xmlChar *key,
986
                    const xmlChar *key2 ATTRIBUTE_UNUSED,
987
13.1k
                    const xmlChar *key3 ATTRIBUTE_UNUSED) {
988
13.1k
    stubData *sdata = (stubData *) data;
989
13.1k
    sdata->scan(payload, sdata->data, key);
990
13.1k
}
991
992
/**
993
 * xmlHashScan:
994
 * @hash: hash table
995
 * @scan: scanner function for items in the hash
996
 * @data: extra data passed to @scan
997
 *
998
 * Scan the hash @table and apply @scan to each value.
999
 */
1000
void
1001
14.6k
xmlHashScan(xmlHashTablePtr hash, xmlHashScanner scan, void *data) {
1002
14.6k
    stubData sdata;
1003
14.6k
    sdata.data = data;
1004
14.6k
    sdata.scan = scan;
1005
14.6k
    xmlHashScanFull(hash, stubHashScannerFull, &sdata);
1006
14.6k
}
1007
1008
/**
1009
 * xmlHashScanFull:
1010
 * @hash: hash table
1011
 * @scan: scanner function for items in the hash
1012
 * @data: extra data passed to @scan
1013
 *
1014
 * Scan the hash @table and apply @scan to each value.
1015
 */
1016
void
1017
40.7k
xmlHashScanFull(xmlHashTablePtr hash, xmlHashScannerFull scan, void *data) {
1018
40.7k
    const xmlHashEntry *entry, *end;
1019
40.7k
    xmlHashEntry old;
1020
40.7k
    unsigned i;
1021
1022
40.7k
    if ((hash == NULL) || (hash->size == 0) || (scan == NULL))
1023
1.79k
        return;
1024
1025
    /*
1026
     * We must handle the case that a scanned entry is removed when executing
1027
     * the callback (xmlCleanSpecialAttr and possibly other places).
1028
     *
1029
     * Find the start of a probe sequence to avoid scanning entries twice if
1030
     * a deletion happens.
1031
     */
1032
38.9k
    entry = hash->table;
1033
38.9k
    end = &hash->table[hash->size];
1034
54.1k
    while (entry->hashValue != 0) {
1035
15.1k
        if (++entry >= end)
1036
0
            entry = hash->table;
1037
15.1k
    }
1038
1039
4.82M
    for (i = 0; i < hash->size; i++) {
1040
4.78M
        if ((entry->hashValue != 0) && (entry->payload != NULL)) {
1041
            /*
1042
             * Make sure to rescan after a possible deletion.
1043
             */
1044
108k
            do {
1045
108k
                old = *entry;
1046
108k
                scan(entry->payload, data, entry->key, entry->key2, entry->key3);
1047
108k
            } while ((entry->hashValue != 0) &&
1048
108k
                     (entry->payload != NULL) &&
1049
108k
                     ((entry->key != old.key) ||
1050
85.9k
                      (entry->key2 != old.key2) ||
1051
85.9k
                      (entry->key3 != old.key3)));
1052
103k
        }
1053
4.78M
        if (++entry >= end)
1054
38.9k
            entry = hash->table;
1055
4.78M
    }
1056
38.9k
}
1057
1058
/**
1059
 * xmlHashScan3:
1060
 * @hash: hash table
1061
 * @key: first string key or NULL
1062
 * @key2: second string key or NULL
1063
 * @key3: third string key or NULL
1064
 * @scan: scanner function for items in the hash
1065
 * @data: extra data passed to @scan
1066
 *
1067
 * Scan the hash @table and apply @scan to each value matching
1068
 * (@key, @key2, @key3) tuple. If one of the keys is null,
1069
 * the comparison is considered to match.
1070
 */
1071
void
1072
xmlHashScan3(xmlHashTablePtr hash, const xmlChar *key,
1073
             const xmlChar *key2, const xmlChar *key3,
1074
0
             xmlHashScanner scan, void *data) {
1075
0
    stubData sdata;
1076
0
    sdata.data = data;
1077
0
    sdata.scan = scan;
1078
0
    xmlHashScanFull3(hash, key, key2, key3, stubHashScannerFull, &sdata);
1079
0
}
1080
1081
/**
1082
 * xmlHashScanFull3:
1083
 * @hash: hash table
1084
 * @key: first string key or NULL
1085
 * @key2: second string key or NULL
1086
 * @key3: third string key or NULL
1087
 * @scan: scanner function for items in the hash
1088
 * @data: extra data passed to @scan
1089
 *
1090
 * Scan the hash @table and apply @scan to each value matching
1091
 * (@key, @key2, @key3) tuple. If one of the keys is null,
1092
 * the comparison is considered to match.
1093
 */
1094
void
1095
xmlHashScanFull3(xmlHashTablePtr hash, const xmlChar *key,
1096
                 const xmlChar *key2, const xmlChar *key3,
1097
0
                 xmlHashScannerFull scan, void *data) {
1098
0
    const xmlHashEntry *entry, *end;
1099
0
    xmlHashEntry old;
1100
0
    unsigned i;
1101
1102
0
    if ((hash == NULL) || (hash->size == 0) || (scan == NULL))
1103
0
        return;
1104
1105
    /*
1106
     * We must handle the case that a scanned entry is removed when executing
1107
     * the callback (xmlCleanSpecialAttr and possibly other places).
1108
     *
1109
     * Find the start of a probe sequence to avoid scanning entries twice if
1110
     * a deletion happens.
1111
     */
1112
0
    entry = hash->table;
1113
0
    end = &hash->table[hash->size];
1114
0
    while (entry->hashValue != 0) {
1115
0
        if (++entry >= end)
1116
0
            entry = hash->table;
1117
0
    }
1118
1119
0
    for (i = 0; i < hash->size; i++) {
1120
0
        if ((entry->hashValue != 0) && (entry->payload != NULL)) {
1121
            /*
1122
             * Make sure to rescan after a possible deletion.
1123
             */
1124
0
            do {
1125
0
                if (((key != NULL) && (strcmp((const char *) key,
1126
0
                                              (const char *) entry->key) != 0)) ||
1127
0
                    ((key2 != NULL) && (!xmlFastStrEqual(key2, entry->key2))) ||
1128
0
                    ((key3 != NULL) && (!xmlFastStrEqual(key3, entry->key3))))
1129
0
                    break;
1130
0
                old = *entry;
1131
0
                scan(entry->payload, data, entry->key, entry->key2, entry->key3);
1132
0
            } while ((entry->hashValue != 0) &&
1133
0
                     (entry->payload != NULL) &&
1134
0
                     ((entry->key != old.key) ||
1135
0
                      (entry->key2 != old.key2) ||
1136
0
                      (entry->key3 != old.key3)));
1137
0
        }
1138
0
        if (++entry >= end)
1139
0
            entry = hash->table;
1140
0
    }
1141
0
}
1142
1143
/*
1144
 * xmlHashCopySafe:
1145
 * @hash: hash table
1146
 * @copyFunc: copier function for items in the hash
1147
 * @deallocFunc: deallocation function in case of errors
1148
 *
1149
 * Copy the hash table using @copyFunc to copy payloads.
1150
 *
1151
 * Available since 2.13.0.
1152
 *
1153
 * Returns the new table or NULL if a memory allocation failed.
1154
 */
1155
xmlHashTablePtr
1156
xmlHashCopySafe(xmlHashTablePtr hash, xmlHashCopier copyFunc,
1157
0
                xmlHashDeallocator deallocFunc) {
1158
0
    const xmlHashEntry *entry, *end;
1159
0
    xmlHashTablePtr ret;
1160
1161
0
    if ((hash == NULL) || (copyFunc == NULL))
1162
0
        return(NULL);
1163
1164
0
    ret = xmlHashCreate(hash->size);
1165
0
    if (ret == NULL)
1166
0
        return(NULL);
1167
1168
0
    if (hash->size == 0)
1169
0
        return(ret);
1170
1171
0
    end = &hash->table[hash->size];
1172
1173
0
    for (entry = hash->table; entry < end; entry++) {
1174
0
        if (entry->hashValue != 0) {
1175
0
            void *copy;
1176
1177
0
            copy = copyFunc(entry->payload, entry->key);
1178
0
            if (copy == NULL)
1179
0
                goto error;
1180
0
            if (xmlHashAdd3(ret, entry->key, entry->key2, entry->key3,
1181
0
                            copy) <= 0) {
1182
0
                if (deallocFunc != NULL)
1183
0
                    deallocFunc(copy, entry->key);
1184
0
                goto error;
1185
0
            }
1186
0
        }
1187
0
    }
1188
1189
0
    return(ret);
1190
1191
0
error:
1192
0
    xmlHashFree(ret, deallocFunc);
1193
0
    return(NULL);
1194
0
}
1195
1196
/*
1197
 * xmlHashCopy:
1198
 * @hash: hash table
1199
 * @copy: copier function for items in the hash
1200
 *
1201
 * DEPRECATED: Leaks memory in error case.
1202
 *
1203
 * Copy the hash table using @copy to copy payloads.
1204
 *
1205
 * Returns the new table or NULL if a memory allocation failed.
1206
 */
1207
xmlHashTablePtr
1208
0
xmlHashCopy(xmlHashTablePtr hash, xmlHashCopier copy) {
1209
0
    return(xmlHashCopySafe(hash, copy, NULL));
1210
0
}
1211
1212
/**
1213
 * xmlHashSize:
1214
 * @hash: hash table
1215
 *
1216
 * Query the number of elements in the hash table.
1217
 *
1218
 * Returns the number of elements in the hash table or
1219
 * -1 in case of error.
1220
 */
1221
int
1222
21.9k
xmlHashSize(xmlHashTablePtr hash) {
1223
21.9k
    if (hash == NULL)
1224
0
        return(-1);
1225
21.9k
    return(hash->nbElems);
1226
21.9k
}
1227
1228
/**
1229
 * xmlHashRemoveEntry:
1230
 * @hash: hash table
1231
 * @key: string key
1232
 * @dealloc: deallocator function for removed item or NULL
1233
 *
1234
 * Find the entry specified by the @key and remove it from the hash table.
1235
 * Payload will be freed with @dealloc.
1236
 *
1237
 * Returns 0 on success and -1 if no entry was found.
1238
 */
1239
int xmlHashRemoveEntry(xmlHashTablePtr hash, const xmlChar *key,
1240
1.53k
                       xmlHashDeallocator dealloc) {
1241
1.53k
    return(xmlHashRemoveEntry3(hash, key, NULL, NULL, dealloc));
1242
1.53k
}
1243
1244
/**
1245
 * xmlHashRemoveEntry2:
1246
 * @hash: hash table
1247
 * @key: first string key
1248
 * @key2: second string key
1249
 * @dealloc: deallocator function for removed item or NULL
1250
 *
1251
 * Remove an entry with two strings as key.
1252
 *
1253
 * See xmlHashRemoveEntry.
1254
 *
1255
 * Returns 0 on success and -1 in case of error.
1256
 */
1257
int
1258
xmlHashRemoveEntry2(xmlHashTablePtr hash, const xmlChar *key,
1259
29.3k
                    const xmlChar *key2, xmlHashDeallocator dealloc) {
1260
29.3k
    return(xmlHashRemoveEntry3(hash, key, key2, NULL, dealloc));
1261
29.3k
}
1262
1263
/**
1264
 * xmlHashRemoveEntry3:
1265
 * @hash: hash table
1266
 * @key: first string key
1267
 * @key2: second string key
1268
 * @key3: third string key
1269
 * @dealloc: deallocator function for removed item or NULL
1270
 *
1271
 * Remove an entry with three strings as key.
1272
 *
1273
 * See xmlHashRemoveEntry.
1274
 *
1275
 * Returns 0 on success and -1 in case of error.
1276
 */
1277
ATTRIBUTE_NO_SANITIZE_INTEGER
1278
int
1279
xmlHashRemoveEntry3(xmlHashTablePtr hash, const xmlChar *key,
1280
                    const xmlChar *key2, const xmlChar *key3,
1281
30.8k
                    xmlHashDeallocator dealloc) {
1282
30.8k
    xmlHashEntry *entry, *cur, *next;
1283
30.8k
    unsigned hashValue, mask, pos, nextpos;
1284
30.8k
    int found;
1285
1286
30.8k
    if ((hash == NULL) || (hash->size == 0) || (key == NULL))
1287
0
        return(-1);
1288
1289
30.8k
    hashValue = xmlHashValue(hash->randomSeed, key, key2, key3, NULL);
1290
30.8k
    entry = xmlHashFindEntry(hash, key, key2, key3, hashValue, &found);
1291
30.8k
    if (!found)
1292
6
        return(-1);
1293
1294
30.8k
    if ((dealloc != NULL) && (entry->payload != NULL))
1295
1.53k
        dealloc(entry->payload, entry->key);
1296
30.8k
    if (hash->dict == NULL) {
1297
2.37k
        if (entry->key)
1298
2.37k
            xmlFree(entry->key);
1299
2.37k
        if (entry->key2)
1300
0
            xmlFree(entry->key2);
1301
2.37k
        if (entry->key3)
1302
0
            xmlFree(entry->key3);
1303
2.37k
    }
1304
1305
    /*
1306
     * Find end of probe sequence. Entries at their initial probe
1307
     * position start a new sequence.
1308
     */
1309
30.8k
    mask = hash->size - 1;
1310
30.8k
    pos = entry - hash->table;
1311
30.8k
    cur = entry;
1312
1313
44.0k
    while (1) {
1314
44.0k
        nextpos = pos + 1;
1315
44.0k
        next = cur + 1;
1316
44.0k
        if ((nextpos & mask) == 0)
1317
3.59k
            next = hash->table;
1318
1319
44.0k
        if ((next->hashValue == 0) ||
1320
44.0k
            (((next->hashValue - nextpos) & mask) == 0))
1321
30.8k
            break;
1322
1323
13.1k
        cur = next;
1324
13.1k
        pos = nextpos;
1325
13.1k
    }
1326
1327
    /*
1328
     * Backward shift
1329
     */
1330
30.8k
    next = entry + 1;
1331
1332
30.8k
    if (cur < entry) {
1333
672
        xmlHashEntry *end = &hash->table[hash->size];
1334
1335
672
        memmove(entry, next, (char *) end - (char *) next);
1336
672
        entry = hash->table;
1337
672
        end[-1] = *entry;
1338
672
        next = entry + 1;
1339
672
    }
1340
1341
30.8k
    memmove(entry, next, (char *) cur - (char *) entry);
1342
1343
    /*
1344
     * Update entry
1345
     */
1346
30.8k
    cur->hashValue = 0;
1347
1348
30.8k
    hash->nbElems--;
1349
1350
30.8k
    return(0);
1351
30.8k
}
1352