Coverage Report

Created: 2025-07-07 10:01

/work/workdir/UnpackedTarball/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
34.7k
#define MAX_FILL_NUM 7
29
34.7k
#define MAX_FILL_DENOM 8
30
16.4k
#define MIN_HASH_SIZE 8
31
314k
#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
366k
             const xmlChar *key3, size_t *lengths) {
63
366k
    unsigned h1, h2;
64
366k
    size_t i;
65
66
366k
    HASH_INIT(h1, h2, seed);
67
68
1.85M
    for (i = 0; key[i] != 0; i++) {
69
1.48M
        HASH_UPDATE(h1, h2, key[i]);
70
1.48M
    }
71
366k
    if (lengths)
72
72.9k
        lengths[0] = i;
73
74
366k
    HASH_UPDATE(h1, h2, 0);
75
76
366k
    if (key2 != NULL) {
77
1.97M
        for (i = 0; key2[i] != 0; i++) {
78
1.88M
            HASH_UPDATE(h1, h2, key2[i]);
79
1.88M
        }
80
88.1k
        if (lengths)
81
45.0k
            lengths[1] = i;
82
88.1k
    }
83
84
366k
    HASH_UPDATE(h1, h2, 0);
85
86
366k
    if (key3 != NULL) {
87
0
        for (i = 0; key3[i] != 0; i++) {
88
0
            HASH_UPDATE(h1, h2, key3[i]);
89
0
        }
90
0
        if (lengths)
91
0
            lengths[2] = i;
92
0
    }
93
94
366k
    HASH_FINISH(h1, h2);
95
96
366k
    return(h2);
97
366k
}
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
21.1k
                  const xmlChar *prefix3, const xmlChar *name3) {
105
21.1k
    unsigned h1, h2, ch;
106
107
21.1k
    HASH_INIT(h1, h2, seed);
108
109
21.1k
    if (prefix != NULL) {
110
36.7k
        while ((ch = *prefix++) != 0) {
111
30.7k
            HASH_UPDATE(h1, h2, ch);
112
30.7k
        }
113
5.95k
        HASH_UPDATE(h1, h2, ':');
114
5.95k
    }
115
21.1k
    if (name != NULL) {
116
113k
        while ((ch = *name++) != 0) {
117
92.2k
            HASH_UPDATE(h1, h2, ch);
118
92.2k
        }
119
21.1k
    }
120
21.1k
    HASH_UPDATE(h1, h2, 0);
121
21.1k
    if (prefix2 != NULL) {
122
40.5k
        while ((ch = *prefix2++) != 0) {
123
33.2k
            HASH_UPDATE(h1, h2, ch);
124
33.2k
        }
125
7.29k
        HASH_UPDATE(h1, h2, ':');
126
7.29k
    }
127
21.1k
    if (name2 != NULL) {
128
100k
        while ((ch = *name2++) != 0) {
129
79.3k
            HASH_UPDATE(h1, h2, ch);
130
79.3k
        }
131
21.1k
    }
132
21.1k
    HASH_UPDATE(h1, h2, 0);
133
21.1k
    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
21.1k
    if (name3 != NULL) {
140
0
        while ((ch = *name3++) != 0) {
141
0
            HASH_UPDATE(h1, h2, ch);
142
0
        }
143
0
    }
144
145
21.1k
    HASH_FINISH(h1, h2);
146
147
21.1k
    return(h2);
148
21.1k
}
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
8.20k
xmlHashCreate(int size) {
161
8.20k
    xmlHashTablePtr hash;
162
163
8.20k
    xmlInitParser();
164
165
8.20k
    hash = xmlMalloc(sizeof(*hash));
166
8.20k
    if (hash == NULL)
167
0
        return(NULL);
168
8.20k
    hash->dict = NULL;
169
8.20k
    hash->size = 0;
170
8.20k
    hash->table = NULL;
171
8.20k
    hash->nbElems = 0;
172
8.20k
    hash->randomSeed = xmlRandom();
173
8.20k
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
174
8.20k
    hash->randomSeed = 0;
175
8.20k
#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
8.20k
    if (size > MIN_HASH_SIZE) {
183
6.46k
        unsigned newSize = MIN_HASH_SIZE * 2;
184
185
6.46k
        while ((newSize < (unsigned) size) && (newSize < MAX_HASH_SIZE))
186
0
            newSize *= 2;
187
188
6.46k
        if (xmlHashGrow(hash, newSize) != 0) {
189
0
            xmlFree(hash);
190
0
            return(NULL);
191
0
        }
192
6.46k
    }
193
194
8.20k
    return(hash);
195
8.20k
}
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
7.61k
xmlHashCreateDict(int size, xmlDictPtr dict) {
210
7.61k
    xmlHashTablePtr hash;
211
212
7.61k
    hash = xmlHashCreate(size);
213
7.61k
    if (hash != NULL) {
214
7.61k
        hash->dict = dict;
215
7.61k
        xmlDictReference(dict);
216
7.61k
    }
217
7.61k
    return(hash);
218
7.61k
}
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
9.38k
xmlHashFree(xmlHashTablePtr hash, xmlHashDeallocator dealloc) {
230
9.38k
    if (hash == NULL)
231
1.17k
        return;
232
233
8.20k
    if (hash->table) {
234
8.20k
        const xmlHashEntry *end = &hash->table[hash->size];
235
8.20k
        const xmlHashEntry *entry;
236
237
146k
        for (entry = hash->table; entry < end; entry++) {
238
138k
            if (entry->hashValue == 0)
239
103k
                continue;
240
34.4k
            if ((dealloc != NULL) && (entry->payload != NULL))
241
10.5k
                dealloc(entry->payload, entry->key);
242
34.4k
            if (hash->dict == NULL) {
243
4.84k
                if (entry->key)
244
4.84k
                    xmlFree(entry->key);
245
4.84k
                if (entry->key2)
246
0
                    xmlFree(entry->key2);
247
4.84k
                if (entry->key3)
248
0
                    xmlFree(entry->key3);
249
4.84k
            }
250
34.4k
        }
251
252
8.20k
        xmlFree(hash->table);
253
8.20k
    }
254
255
8.20k
    if (hash->dict)
256
5.87k
        xmlDictFree(hash->dict);
257
258
8.20k
    xmlFree(hash);
259
8.20k
}
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
41.2k
xmlFastStrEqual(const xmlChar *s1, const xmlChar *s2) {
270
41.2k
    if (s1 == NULL)
271
41.2k
        return(s2 == NULL);
272
0
    else
273
0
        return((s2 != NULL) &&
274
0
               (strcmp((const char *) s1, (const char *) s2) == 0));
275
41.2k
}
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
364k
                 unsigned hashValue, int *pfound) {
295
364k
    xmlHashEntry *entry;
296
364k
    unsigned mask, pos, displ;
297
364k
    int found = 0;
298
299
364k
    mask = hash->size - 1;
300
364k
    pos = hashValue & mask;
301
364k
    entry = &hash->table[pos];
302
303
364k
    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
263k
        displ = 0;
310
263k
        hashValue |= MAX_HASH_SIZE;
311
312
299k
        do {
313
299k
            if (entry->hashValue == hashValue) {
314
236k
                if (hash->dict) {
315
215k
                    if ((entry->key == key) &&
316
215k
                        (entry->key2 == key2) &&
317
215k
                        (entry->key3 == key3)) {
318
215k
                        found = 1;
319
215k
                        break;
320
215k
                    }
321
215k
                }
322
20.6k
                if ((strcmp((const char *) entry->key,
323
20.6k
                            (const char *) key) == 0) &&
324
20.6k
                    (xmlFastStrEqual(entry->key2, key2)) &&
325
20.6k
                    (xmlFastStrEqual(entry->key3, key3))) {
326
20.6k
                    found = 1;
327
20.6k
                    break;
328
20.6k
                }
329
20.6k
            }
330
331
63.3k
            displ++;
332
63.3k
            pos++;
333
63.3k
            entry++;
334
63.3k
            if ((pos & mask) == 0)
335
4.94k
                entry = hash->table;
336
63.3k
        } while ((entry->hashValue != 0) &&
337
63.3k
                 (((pos - entry->hashValue) & mask) >= displ));
338
263k
    }
339
340
0
    *pfound = found;
341
364k
    return(entry);
342
364k
}
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
8.98k
xmlHashGrow(xmlHashTablePtr hash, unsigned size) {
355
8.98k
    const xmlHashEntry *oldentry, *oldend, *end;
356
8.98k
    xmlHashEntry *table;
357
8.98k
    unsigned oldsize, i;
358
359
    /* Add 0 to avoid spurious -Wtype-limits warning on 64-bit GCC */
360
8.98k
    if ((size_t) size + 0 > SIZE_MAX / sizeof(table[0]))
361
0
        return(-1);
362
8.98k
    table = xmlMalloc(size * sizeof(table[0]));
363
8.98k
    if (table == NULL)
364
0
        return(-1);
365
8.98k
    memset(table, 0, size * sizeof(table[0]));
366
367
8.98k
    oldsize = hash->size;
368
8.98k
    if (oldsize == 0)
369
8.20k
        goto done;
370
371
777
    oldend = &hash->table[oldsize];
372
777
    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
777
    oldentry = hash->table;
382
6.53k
    while (oldentry->hashValue != 0) {
383
5.75k
        if (++oldentry >= oldend)
384
0
            oldentry = hash->table;
385
5.75k
    }
386
387
21.5k
    for (i = 0; i < oldsize; i++) {
388
20.7k
        if (oldentry->hashValue != 0) {
389
18.1k
            xmlHashEntry *entry = &table[oldentry->hashValue & (size - 1)];
390
391
24.3k
            while (entry->hashValue != 0) {
392
6.14k
                if (++entry >= end)
393
142
                    entry = table;
394
6.14k
            }
395
18.1k
            *entry = *oldentry;
396
18.1k
        }
397
398
20.7k
        if (++oldentry >= oldend)
399
777
            oldentry = hash->table;
400
20.7k
    }
401
402
777
    xmlFree(hash->table);
403
404
8.98k
done:
405
8.98k
    hash->table = table;
406
8.98k
    hash->size = size;
407
408
8.98k
    return(0);
409
777
}
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
72.9k
                      void *payload, xmlHashDeallocator dealloc, int update) {
428
72.9k
    xmlChar *copy, *copy2, *copy3;
429
72.9k
    xmlHashEntry *entry = NULL;
430
72.9k
    size_t lengths[3] = {0, 0, 0};
431
72.9k
    unsigned hashValue, newSize;
432
433
72.9k
    if ((hash == NULL) || (key == NULL))
434
0
        return(-1);
435
436
72.9k
    hashValue = xmlHashValue(hash->randomSeed, key, key2, key3, lengths);
437
438
    /*
439
     * Check for an existing entry
440
     */
441
72.9k
    if (hash->size == 0) {
442
1.74k
        newSize = MIN_HASH_SIZE;
443
71.1k
    } else {
444
71.1k
        int found = 0;
445
446
71.1k
        entry = xmlHashFindEntry(hash, key, key2, key3, hashValue, &found);
447
448
71.1k
        if (found) {
449
36.4k
            if (update) {
450
2.55k
                if (dealloc)
451
0
                    dealloc(entry->payload, entry->key);
452
2.55k
                entry->payload = payload;
453
2.55k
            }
454
455
36.4k
            return(0);
456
36.4k
        }
457
458
34.7k
        if (hash->nbElems + 1 > hash->size / MAX_FILL_DENOM * MAX_FILL_NUM) {
459
            /* This guarantees that nbElems < INT_MAX */
460
777
            if (hash->size >= MAX_HASH_SIZE)
461
0
                return(-1);
462
777
            newSize = hash->size * 2;
463
33.9k
        } else {
464
33.9k
            newSize = 0;
465
33.9k
        }
466
34.7k
    }
467
468
    /*
469
     * Grow the hash table if needed
470
     */
471
36.4k
    if (newSize > 0) {
472
2.52k
        unsigned mask, displ, pos;
473
474
2.52k
        if (xmlHashGrow(hash, newSize) != 0)
475
0
            return(-1);
476
477
        /*
478
         * Find new entry
479
         */
480
2.52k
        mask = hash->size - 1;
481
2.52k
        displ = 0;
482
2.52k
        pos = hashValue & mask;
483
2.52k
        entry = &hash->table[pos];
484
485
2.52k
        if (entry->hashValue != 0) {
486
533
            do {
487
533
                displ++;
488
533
                pos++;
489
533
                entry++;
490
533
                if ((pos & mask) == 0)
491
21
                    entry = hash->table;
492
533
            } while ((entry->hashValue != 0) &&
493
533
                     ((pos - entry->hashValue) & mask) >= displ);
494
389
        }
495
2.52k
    }
496
497
    /*
498
     * Copy keys
499
     */
500
36.4k
    if (hash->dict != NULL) {
501
31.6k
        if (xmlDictOwns(hash->dict, key)) {
502
31.6k
            copy = (xmlChar *) key;
503
31.6k
        } else {
504
0
            copy = (xmlChar *) xmlDictLookup(hash->dict, key, -1);
505
0
            if (copy == NULL)
506
0
                return(-1);
507
0
        }
508
509
31.6k
        if ((key2 == NULL) || (xmlDictOwns(hash->dict, key2))) {
510
31.6k
            copy2 = (xmlChar *) key2;
511
31.6k
        } else {
512
0
            copy2 = (xmlChar *) xmlDictLookup(hash->dict, key2, -1);
513
0
            if (copy2 == NULL)
514
0
                return(-1);
515
0
        }
516
31.6k
        if ((key3 == NULL) || (xmlDictOwns(hash->dict, key3))) {
517
31.6k
            copy3 = (xmlChar *) key3;
518
31.6k
        } else {
519
0
            copy3 = (xmlChar *) xmlDictLookup(hash->dict, key3, -1);
520
0
            if (copy3 == NULL)
521
0
                return(-1);
522
0
        }
523
31.6k
    } else {
524
4.84k
        copy = xmlMalloc(lengths[0] + 1);
525
4.84k
        if (copy == NULL)
526
0
            return(-1);
527
4.84k
        memcpy(copy, key, lengths[0] + 1);
528
529
4.84k
        if (key2 != NULL) {
530
0
            copy2 = xmlMalloc(lengths[1] + 1);
531
0
            if (copy2 == NULL) {
532
0
                xmlFree(copy);
533
0
                return(-1);
534
0
            }
535
0
            memcpy(copy2, key2, lengths[1] + 1);
536
4.84k
        } else {
537
4.84k
            copy2 = NULL;
538
4.84k
        }
539
540
4.84k
        if (key3 != NULL) {
541
0
            copy3 = xmlMalloc(lengths[2] + 1);
542
0
            if (copy3 == NULL) {
543
0
                xmlFree(copy);
544
0
                xmlFree(copy2);
545
0
                return(-1);
546
0
            }
547
0
            memcpy(copy3, key3, lengths[2] + 1);
548
4.84k
        } else {
549
4.84k
            copy3 = NULL;
550
4.84k
        }
551
4.84k
    }
552
553
    /*
554
     * Shift the remainder of the probe sequence to the right
555
     */
556
36.4k
    if (entry->hashValue != 0) {
557
6.38k
        const xmlHashEntry *end = &hash->table[hash->size];
558
6.38k
        const xmlHashEntry *cur = entry;
559
560
28.9k
        do {
561
28.9k
            cur++;
562
28.9k
            if (cur >= end)
563
916
                cur = hash->table;
564
28.9k
        } while (cur->hashValue != 0);
565
566
6.38k
        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
916
            memmove(&hash->table[1], hash->table,
572
916
                    (char *) cur - (char *) hash->table);
573
916
            cur = end - 1;
574
916
            hash->table[0] = *cur;
575
916
        }
576
577
6.38k
        memmove(&entry[1], entry, (char *) cur - (char *) entry);
578
6.38k
    }
579
580
    /*
581
     * Populate entry
582
     */
583
36.4k
    entry->key = copy;
584
36.4k
    entry->key2 = copy2;
585
36.4k
    entry->key3 = copy3;
586
36.4k
    entry->payload = payload;
587
    /* OR with MAX_HASH_SIZE to make sure that the value is non-zero */
588
36.4k
    entry->hashValue = hashValue | MAX_HASH_SIZE;
589
590
36.4k
    hash->nbElems++;
591
592
36.4k
    return(1);
593
36.4k
}
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
6.68k
xmlHashDefaultDeallocator(void *entry, const xmlChar *key ATTRIBUTE_UNUSED) {
604
6.68k
    xmlFree(entry);
605
6.68k
}
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
19.5k
xmlHashAdd(xmlHashTablePtr hash, const xmlChar *key, void *payload) {
624
19.5k
    return(xmlHashUpdateInternal(hash, key, NULL, NULL, payload, NULL, 0));
625
19.5k
}
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
43.1k
                 const xmlChar *key2, void *payload) {
645
43.1k
    return(xmlHashUpdateInternal(hash, key, key2, NULL, payload, NULL, 0));
646
43.1k
}
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
0
                 void *payload) {
668
0
    return(xmlHashUpdateInternal(hash, key, key2, key3, payload, NULL, 0));
669
0
}
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
933
xmlHashAddEntry(xmlHashTablePtr hash, const xmlChar *key, void *payload) {
689
933
    int res = xmlHashUpdateInternal(hash, key, NULL, NULL, payload, NULL, 0);
690
691
933
    if (res == 0)
692
0
        res = -1;
693
933
    else if (res == 1)
694
933
        res = 0;
695
696
933
    return(res);
697
933
}
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
0
                 const xmlChar *key2, void *payload) {
715
0
    int res = xmlHashUpdateInternal(hash, key, key2, NULL, payload, NULL, 0);
716
717
0
    if (res == 0)
718
0
        res = -1;
719
0
    else if (res == 1)
720
0
        res = 0;
721
722
0
    return(res);
723
0
}
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
0
                 void *payload) {
743
0
    int res = xmlHashUpdateInternal(hash, key, key2, key3, payload, NULL, 0);
744
745
0
    if (res == 0)
746
0
        res = -1;
747
0
    else if (res == 1)
748
0
        res = 0;
749
750
0
    return(res);
751
0
}
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
975
                   void *payload, xmlHashDeallocator dealloc) {
768
975
    int res = xmlHashUpdateInternal(hash, key, NULL, NULL, payload,
769
975
                                    dealloc, 1);
770
771
975
    if (res == 1)
772
975
        res = 0;
773
774
975
    return(res);
775
975
}
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
8.26k
                   xmlHashDeallocator dealloc) {
795
8.26k
    int res = xmlHashUpdateInternal(hash, key, key2, NULL, payload,
796
8.26k
                                    dealloc, 1);
797
798
8.26k
    if (res == 1)
799
5.71k
        res = 0;
800
801
8.26k
    return(res);
802
8.26k
}
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
0
                   void *payload, xmlHashDeallocator dealloc) {
823
0
    int res = xmlHashUpdateInternal(hash, key, key2, key3, payload,
824
0
                                    dealloc, 1);
825
826
0
    if (res == 1)
827
0
        res = 0;
828
829
0
    return(res);
830
0
}
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
4.63k
xmlHashLookup(xmlHashTablePtr hash, const xmlChar *key) {
843
4.63k
    return(xmlHashLookup3(hash, key, NULL, NULL));
844
4.63k
}
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
286k
              const xmlChar *key2) {
859
286k
    return(xmlHashLookup3(hash, key, key2, NULL));
860
286k
}
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
21.1k
                const xmlChar *name2) {
894
21.1k
    return(xmlHashQLookup3(hash, prefix, name, prefix2, name2, NULL, NULL));
895
21.1k
}
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
291k
               const xmlChar *key2, const xmlChar *key3) {
911
291k
    const xmlHashEntry *entry;
912
291k
    unsigned hashValue;
913
291k
    int found;
914
915
291k
    if ((hash == NULL) || (hash->size == 0) || (key == NULL))
916
0
        return(NULL);
917
291k
    hashValue = xmlHashValue(hash->randomSeed, key, key2, key3, NULL);
918
291k
    entry = xmlHashFindEntry(hash, key, key2, key3, hashValue, &found);
919
291k
    if (found)
920
197k
        return(entry->payload);
921
93.5k
    return(NULL);
922
291k
}
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
21.1k
                const xmlChar *prefix3, const xmlChar *name3) {
944
21.1k
    const xmlHashEntry *entry;
945
21.1k
    unsigned hashValue, mask, pos, displ;
946
947
21.1k
    if ((hash == NULL) || (hash->size == 0) || (name == NULL))
948
0
        return(NULL);
949
950
21.1k
    hashValue = xmlHashQNameValue(hash->randomSeed, prefix, name, prefix2,
951
21.1k
                                  name2, prefix3, name3);
952
21.1k
    mask = hash->size - 1;
953
21.1k
    pos = hashValue & mask;
954
21.1k
    entry = &hash->table[pos];
955
956
21.1k
    if (entry->hashValue != 0) {
957
13.8k
        displ = 0;
958
13.8k
        hashValue |= MAX_HASH_SIZE;
959
960
17.2k
        do {
961
17.2k
            if ((hashValue == entry->hashValue) &&
962
17.2k
                (xmlStrQEqual(prefix, name, entry->key)) &&
963
17.2k
                (xmlStrQEqual(prefix2, name2, entry->key2)) &&
964
17.2k
                (xmlStrQEqual(prefix3, name3, entry->key3)))
965
9.32k
                return(entry->payload);
966
967
7.91k
            displ++;
968
7.91k
            pos++;
969
7.91k
            entry++;
970
7.91k
            if ((pos & mask) == 0)
971
819
                entry = hash->table;
972
7.91k
        } while ((entry->hashValue != 0) &&
973
7.91k
                 (((pos - entry->hashValue) & mask) >= displ));
974
13.8k
    }
975
976
11.8k
    return(NULL);
977
21.1k
}
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
0
                    const xmlChar *key3 ATTRIBUTE_UNUSED) {
988
0
    stubData *sdata = (stubData *) data;
989
0
    sdata->scan(payload, sdata->data, key);
990
0
}
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
0
xmlHashScan(xmlHashTablePtr hash, xmlHashScanner scan, void *data) {
1002
0
    stubData sdata;
1003
0
    sdata.data = data;
1004
0
    sdata.scan = scan;
1005
0
    xmlHashScanFull(hash, stubHashScannerFull, &sdata);
1006
0
}
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
3.14k
xmlHashScanFull(xmlHashTablePtr hash, xmlHashScannerFull scan, void *data) {
1018
3.14k
    const xmlHashEntry *entry, *end;
1019
3.14k
    xmlHashEntry old;
1020
3.14k
    unsigned i;
1021
1022
3.14k
    if ((hash == NULL) || (hash->size == 0) || (scan == NULL))
1023
0
        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
3.14k
    entry = hash->table;
1033
3.14k
    end = &hash->table[hash->size];
1034
6.25k
    while (entry->hashValue != 0) {
1035
3.10k
        if (++entry >= end)
1036
0
            entry = hash->table;
1037
3.10k
    }
1038
1039
69.4k
    for (i = 0; i < hash->size; i++) {
1040
66.3k
        if ((entry->hashValue != 0) && (entry->payload != NULL)) {
1041
            /*
1042
             * Make sure to rescan after a possible deletion.
1043
             */
1044
25.9k
            do {
1045
25.9k
                old = *entry;
1046
25.9k
                scan(entry->payload, data, entry->key, entry->key2, entry->key3);
1047
25.9k
            } while ((entry->hashValue != 0) &&
1048
25.9k
                     (entry->payload != NULL) &&
1049
25.9k
                     ((entry->key != old.key) ||
1050
24.4k
                      (entry->key2 != old.key2) ||
1051
24.4k
                      (entry->key3 != old.key3)));
1052
25.4k
        }
1053
66.3k
        if (++entry >= end)
1054
3.14k
            entry = hash->table;
1055
66.3k
    }
1056
3.14k
}
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
3.14k
xmlHashSize(xmlHashTablePtr hash) {
1223
3.14k
    if (hash == NULL)
1224
0
        return(-1);
1225
3.14k
    return(hash->nbElems);
1226
3.14k
}
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
0
                       xmlHashDeallocator dealloc) {
1241
0
    return(xmlHashRemoveEntry3(hash, key, NULL, NULL, dealloc));
1242
0
}
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
2.01k
                    const xmlChar *key2, xmlHashDeallocator dealloc) {
1260
2.01k
    return(xmlHashRemoveEntry3(hash, key, key2, NULL, dealloc));
1261
2.01k
}
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
2.01k
                    xmlHashDeallocator dealloc) {
1282
2.01k
    xmlHashEntry *entry, *cur, *next;
1283
2.01k
    unsigned hashValue, mask, pos, nextpos;
1284
2.01k
    int found;
1285
1286
2.01k
    if ((hash == NULL) || (hash->size == 0) || (key == NULL))
1287
0
        return(-1);
1288
1289
2.01k
    hashValue = xmlHashValue(hash->randomSeed, key, key2, key3, NULL);
1290
2.01k
    entry = xmlHashFindEntry(hash, key, key2, key3, hashValue, &found);
1291
2.01k
    if (!found)
1292
0
        return(-1);
1293
1294
2.01k
    if ((dealloc != NULL) && (entry->payload != NULL))
1295
0
        dealloc(entry->payload, entry->key);
1296
2.01k
    if (hash->dict == NULL) {
1297
0
        if (entry->key)
1298
0
            xmlFree(entry->key);
1299
0
        if (entry->key2)
1300
0
            xmlFree(entry->key2);
1301
0
        if (entry->key3)
1302
0
            xmlFree(entry->key3);
1303
0
    }
1304
1305
    /*
1306
     * Find end of probe sequence. Entries at their initial probe
1307
     * position start a new sequence.
1308
     */
1309
2.01k
    mask = hash->size - 1;
1310
2.01k
    pos = entry - hash->table;
1311
2.01k
    cur = entry;
1312
1313
3.75k
    while (1) {
1314
3.75k
        nextpos = pos + 1;
1315
3.75k
        next = cur + 1;
1316
3.75k
        if ((nextpos & mask) == 0)
1317
228
            next = hash->table;
1318
1319
3.75k
        if ((next->hashValue == 0) ||
1320
3.75k
            (((next->hashValue - nextpos) & mask) == 0))
1321
2.01k
            break;
1322
1323
1.74k
        cur = next;
1324
1.74k
        pos = nextpos;
1325
1.74k
    }
1326
1327
    /*
1328
     * Backward shift
1329
     */
1330
2.01k
    next = entry + 1;
1331
1332
2.01k
    if (cur < entry) {
1333
100
        xmlHashEntry *end = &hash->table[hash->size];
1334
1335
100
        memmove(entry, next, (char *) end - (char *) next);
1336
100
        entry = hash->table;
1337
100
        end[-1] = *entry;
1338
100
        next = entry + 1;
1339
100
    }
1340
1341
2.01k
    memmove(entry, next, (char *) cur - (char *) entry);
1342
1343
    /*
1344
     * Update entry
1345
     */
1346
2.01k
    cur->hashValue = 0;
1347
1348
2.01k
    hash->nbElems--;
1349
1350
2.01k
    return(0);
1351
2.01k
}
1352