Coverage Report

Created: 2025-07-23 08:13

/src/fontconfig/subprojects/libxml2-2.12.6/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
0
  #define SIZE_MAX ((size_t) -1)
26
#endif
27
28
0
#define MAX_FILL_NUM 7
29
0
#define MAX_FILL_DENOM 8
30
0
#define MIN_HASH_SIZE 8
31
0
#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
0
             const xmlChar *key3, size_t *lengths) {
63
0
    unsigned h1, h2;
64
0
    size_t i;
65
66
0
    HASH_INIT(h1, h2, seed);
67
68
0
    for (i = 0; key[i] != 0; i++) {
69
0
        HASH_UPDATE(h1, h2, key[i]);
70
0
    }
71
0
    if (lengths)
72
0
        lengths[0] = i;
73
74
0
    HASH_UPDATE(h1, h2, 0);
75
76
0
    if (key2 != NULL) {
77
0
        for (i = 0; key2[i] != 0; i++) {
78
0
            HASH_UPDATE(h1, h2, key2[i]);
79
0
        }
80
0
        if (lengths)
81
0
            lengths[1] = i;
82
0
    }
83
84
0
    HASH_UPDATE(h1, h2, 0);
85
86
0
    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
0
    HASH_FINISH(h1, h2);
95
96
0
    return(h2);
97
0
}
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
0
                  const xmlChar *prefix3, const xmlChar *name3) {
105
0
    unsigned h1, h2, ch;
106
107
0
    HASH_INIT(h1, h2, seed);
108
109
0
    if (prefix != NULL) {
110
0
        while ((ch = *prefix++) != 0) {
111
0
            HASH_UPDATE(h1, h2, ch);
112
0
        }
113
0
        HASH_UPDATE(h1, h2, ':');
114
0
    }
115
0
    if (name != NULL) {
116
0
        while ((ch = *name++) != 0) {
117
0
            HASH_UPDATE(h1, h2, ch);
118
0
        }
119
0
    }
120
0
    HASH_UPDATE(h1, h2, 0);
121
0
    if (prefix2 != NULL) {
122
0
        while ((ch = *prefix2++) != 0) {
123
0
            HASH_UPDATE(h1, h2, ch);
124
0
        }
125
0
        HASH_UPDATE(h1, h2, ':');
126
0
    }
127
0
    if (name2 != NULL) {
128
0
        while ((ch = *name2++) != 0) {
129
0
            HASH_UPDATE(h1, h2, ch);
130
0
        }
131
0
    }
132
0
    HASH_UPDATE(h1, h2, 0);
133
0
    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
0
    if (name3 != NULL) {
140
0
        while ((ch = *name3++) != 0) {
141
0
            HASH_UPDATE(h1, h2, ch);
142
0
        }
143
0
    }
144
145
0
    HASH_FINISH(h1, h2);
146
147
0
    return(h2);
148
0
}
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
0
xmlHashCreate(int size) {
161
0
    xmlHashTablePtr hash;
162
163
0
    xmlInitParser();
164
165
0
    hash = xmlMalloc(sizeof(*hash));
166
0
    if (hash == NULL)
167
0
        return(NULL);
168
0
    hash->dict = NULL;
169
0
    hash->size = 0;
170
0
    hash->table = NULL;
171
0
    hash->nbElems = 0;
172
0
    hash->randomSeed = xmlRandom();
173
0
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
174
0
    hash->randomSeed = 0;
175
0
#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
0
    if (size > MIN_HASH_SIZE) {
183
0
        unsigned newSize = MIN_HASH_SIZE * 2;
184
185
0
        while ((newSize < (unsigned) size) && (newSize < MAX_HASH_SIZE))
186
0
            newSize *= 2;
187
188
0
        if (xmlHashGrow(hash, newSize) != 0) {
189
0
            xmlFree(hash);
190
0
            return(NULL);
191
0
        }
192
0
    }
193
194
0
    return(hash);
195
0
}
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
0
xmlHashCreateDict(int size, xmlDictPtr dict) {
210
0
    xmlHashTablePtr hash;
211
212
0
    hash = xmlHashCreate(size);
213
0
    if (hash != NULL) {
214
0
        hash->dict = dict;
215
0
        xmlDictReference(dict);
216
0
    }
217
0
    return(hash);
218
0
}
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
0
xmlHashFree(xmlHashTablePtr hash, xmlHashDeallocator dealloc) {
230
0
    if (hash == NULL)
231
0
        return;
232
233
0
    if (hash->table) {
234
0
        const xmlHashEntry *end = &hash->table[hash->size];
235
0
        const xmlHashEntry *entry;
236
237
0
        for (entry = hash->table; entry < end; entry++) {
238
0
            if (entry->hashValue == 0)
239
0
                continue;
240
0
            if ((dealloc != NULL) && (entry->payload != NULL))
241
0
                dealloc(entry->payload, entry->key);
242
0
            if (hash->dict == NULL) {
243
0
                if (entry->key)
244
0
                    xmlFree(entry->key);
245
0
                if (entry->key2)
246
0
                    xmlFree(entry->key2);
247
0
                if (entry->key3)
248
0
                    xmlFree(entry->key3);
249
0
            }
250
0
        }
251
252
0
        xmlFree(hash->table);
253
0
    }
254
255
0
    if (hash->dict)
256
0
        xmlDictFree(hash->dict);
257
258
0
    xmlFree(hash);
259
0
}
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
0
xmlFastStrEqual(const xmlChar *s1, const xmlChar *s2) {
270
0
    if (s1 == NULL)
271
0
        return(s2 == NULL);
272
0
    else
273
0
        return((s2 != NULL) &&
274
0
               (strcmp((const char *) s1, (const char *) s2) == 0));
275
0
}
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
0
                 unsigned hashValue, int *pfound) {
295
0
    xmlHashEntry *entry;
296
0
    unsigned mask, pos, displ;
297
0
    int found = 0;
298
299
0
    mask = hash->size - 1;
300
0
    pos = hashValue & mask;
301
0
    entry = &hash->table[pos];
302
303
0
    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
0
        displ = 0;
310
0
        hashValue |= MAX_HASH_SIZE;
311
312
0
        do {
313
0
            if (entry->hashValue == hashValue) {
314
0
                if (hash->dict) {
315
0
                    if ((entry->key == key) &&
316
0
                        (entry->key2 == key2) &&
317
0
                        (entry->key3 == key3)) {
318
0
                        found = 1;
319
0
                        break;
320
0
                    }
321
0
                }
322
0
                if ((strcmp((const char *) entry->key,
323
0
                            (const char *) key) == 0) &&
324
0
                    (xmlFastStrEqual(entry->key2, key2)) &&
325
0
                    (xmlFastStrEqual(entry->key3, key3))) {
326
0
                    found = 1;
327
0
                    break;
328
0
                }
329
0
            }
330
331
0
            displ++;
332
0
            pos++;
333
0
            entry++;
334
0
            if ((pos & mask) == 0)
335
0
                entry = hash->table;
336
0
        } while ((entry->hashValue != 0) &&
337
0
                 (((pos - entry->hashValue) & mask) >= displ));
338
0
    }
339
340
0
    *pfound = found;
341
0
    return(entry);
342
0
}
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
0
xmlHashGrow(xmlHashTablePtr hash, unsigned size) {
355
0
    const xmlHashEntry *oldentry, *oldend, *end;
356
0
    xmlHashEntry *table;
357
0
    unsigned oldsize, i;
358
359
    /* Add 0 to avoid spurious -Wtype-limits warning on 64-bit GCC */
360
0
    if ((size_t) size + 0 > SIZE_MAX / sizeof(table[0]))
361
0
        return(-1);
362
0
    table = xmlMalloc(size * sizeof(table[0]));
363
0
    if (table == NULL)
364
0
        return(-1);
365
0
    memset(table, 0, size * sizeof(table[0]));
366
367
0
    oldsize = hash->size;
368
0
    if (oldsize == 0)
369
0
        goto done;
370
371
0
    oldend = &hash->table[oldsize];
372
0
    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
0
    oldentry = hash->table;
382
0
    while (oldentry->hashValue != 0) {
383
0
        if (++oldentry >= oldend)
384
0
            oldentry = hash->table;
385
0
    }
386
387
0
    for (i = 0; i < oldsize; i++) {
388
0
        if (oldentry->hashValue != 0) {
389
0
            xmlHashEntry *entry = &table[oldentry->hashValue & (size - 1)];
390
391
0
            while (entry->hashValue != 0) {
392
0
                if (++entry >= end)
393
0
                    entry = table;
394
0
            }
395
0
            *entry = *oldentry;
396
0
        }
397
398
0
        if (++oldentry >= oldend)
399
0
            oldentry = hash->table;
400
0
    }
401
402
0
    xmlFree(hash->table);
403
404
0
done:
405
0
    hash->table = table;
406
0
    hash->size = size;
407
408
0
    return(0);
409
0
}
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
0
                      void *payload, xmlHashDeallocator dealloc, int update) {
428
0
    xmlChar *copy, *copy2, *copy3;
429
0
    xmlHashEntry *entry = NULL;
430
0
    size_t lengths[3];
431
0
    unsigned hashValue;
432
0
    int found = 0;
433
434
0
    if ((hash == NULL) || (key == NULL))
435
0
        return(-1);
436
437
    /*
438
     * Check for an existing entry
439
     */
440
0
    hashValue = xmlHashValue(hash->randomSeed, key, key2, key3, lengths);
441
0
    if (hash->size > 0)
442
0
        entry = xmlHashFindEntry(hash, key, key2, key3, hashValue, &found);
443
0
    if (found) {
444
0
        if (update) {
445
0
            if (dealloc)
446
0
                dealloc(entry->payload, entry->key);
447
0
            entry->payload = payload;
448
0
            return(0);
449
0
        } else {
450
            /*
451
             * xmlHashAddEntry found an existing entry.
452
             *
453
             * TODO: We should return a different error code here to
454
             * distinguish from malloc failures.
455
             */
456
0
            return(-1);
457
0
        }
458
0
    }
459
460
    /*
461
     * Grow the hash table if needed
462
     */
463
0
    if (hash->nbElems + 1 > hash->size / MAX_FILL_DENOM * MAX_FILL_NUM) {
464
0
        unsigned newSize, mask, displ, pos;
465
466
0
        if (hash->size == 0) {
467
0
            newSize = MIN_HASH_SIZE;
468
0
        } else {
469
            /* This guarantees that nbElems < INT_MAX */
470
0
            if (hash->size >= MAX_HASH_SIZE)
471
0
                return(-1);
472
0
            newSize = hash->size * 2;
473
0
        }
474
0
        if (xmlHashGrow(hash, newSize) != 0)
475
0
            return(-1);
476
477
        /*
478
         * Find new entry
479
         */
480
0
        mask = hash->size - 1;
481
0
        displ = 0;
482
0
        pos = hashValue & mask;
483
0
        entry = &hash->table[pos];
484
485
0
        if (entry->hashValue != 0) {
486
0
            do {
487
0
                displ++;
488
0
                pos++;
489
0
                entry++;
490
0
                if ((pos & mask) == 0)
491
0
                    entry = hash->table;
492
0
            } while ((entry->hashValue != 0) &&
493
0
                     ((pos - entry->hashValue) & mask) >= displ);
494
0
        }
495
0
    }
496
497
    /*
498
     * Copy keys
499
     */
500
0
    if (hash->dict != NULL) {
501
0
        if (xmlDictOwns(hash->dict, key)) {
502
0
            copy = (xmlChar *) key;
503
0
        } else {
504
0
            copy = (xmlChar *) xmlDictLookup(hash->dict, key, -1);
505
0
            if (copy == NULL)
506
0
                return(-1);
507
0
        }
508
509
0
        if ((key2 == NULL) || (xmlDictOwns(hash->dict, key2))) {
510
0
            copy2 = (xmlChar *) key2;
511
0
        } else {
512
0
            copy2 = (xmlChar *) xmlDictLookup(hash->dict, key2, -1);
513
0
            if (copy2 == NULL)
514
0
                return(-1);
515
0
        }
516
0
        if ((key3 == NULL) || (xmlDictOwns(hash->dict, key3))) {
517
0
            copy3 = (xmlChar *) key3;
518
0
        } else {
519
0
            copy3 = (xmlChar *) xmlDictLookup(hash->dict, key3, -1);
520
0
            if (copy3 == NULL)
521
0
                return(-1);
522
0
        }
523
0
    } else {
524
0
        copy = xmlMalloc(lengths[0] + 1);
525
0
        if (copy == NULL)
526
0
            return(-1);
527
0
        memcpy(copy, key, lengths[0] + 1);
528
529
0
        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
0
        } else {
537
0
            copy2 = NULL;
538
0
        }
539
540
0
        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
0
        } else {
549
0
            copy3 = NULL;
550
0
        }
551
0
    }
552
553
    /*
554
     * Shift the remainder of the probe sequence to the right
555
     */
556
0
    if (entry->hashValue != 0) {
557
0
        const xmlHashEntry *end = &hash->table[hash->size];
558
0
        const xmlHashEntry *cur = entry;
559
560
0
        do {
561
0
            cur++;
562
0
            if (cur >= end)
563
0
                cur = hash->table;
564
0
        } while (cur->hashValue != 0);
565
566
0
        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
0
            memmove(&hash->table[1], hash->table,
572
0
                    (char *) cur - (char *) hash->table);
573
0
            cur = end - 1;
574
0
            hash->table[0] = *cur;
575
0
        }
576
577
0
        memmove(&entry[1], entry, (char *) cur - (char *) entry);
578
0
    }
579
580
    /*
581
     * Populate entry
582
     */
583
0
    entry->key = copy;
584
0
    entry->key2 = copy2;
585
0
    entry->key3 = copy3;
586
0
    entry->payload = payload;
587
    /* OR with MAX_HASH_SIZE to make sure that the value is non-zero */
588
0
    entry->hashValue = hashValue | MAX_HASH_SIZE;
589
590
0
    hash->nbElems++;
591
592
0
    return(0);
593
0
}
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
0
xmlHashDefaultDeallocator(void *entry, const xmlChar *key ATTRIBUTE_UNUSED) {
604
0
    xmlFree(entry);
605
0
}
606
607
/**
608
 * xmlHashAddEntry:
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 -1 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
 * Returns 0 on success and -1 in case of error.
619
 */
620
int
621
0
xmlHashAddEntry(xmlHashTablePtr hash, const xmlChar *key, void *payload) {
622
0
    return(xmlHashUpdateInternal(hash, key, NULL, NULL, payload, NULL, 0));
623
0
}
624
625
/**
626
 * xmlHashAddEntry2:
627
 * @hash: hash table
628
 * @key: first string key
629
 * @key2: second string key
630
 * @payload: pointer to the payload
631
 *
632
 * Add a hash table entry with two strings as key.
633
 *
634
 * See xmlHashAddEntry.
635
 *
636
 * Returns 0 on success and -1 in case of error.
637
 */
638
int
639
xmlHashAddEntry2(xmlHashTablePtr hash, const xmlChar *key,
640
0
                 const xmlChar *key2, void *payload) {
641
0
    return(xmlHashUpdateInternal(hash, key, key2, NULL, payload, NULL, 0));
642
0
}
643
644
/**
645
 * xmlHashAddEntry3:
646
 * @hash: hash table
647
 * @key: first string key
648
 * @key2: second string key
649
 * @key3: third string key
650
 * @payload: pointer to the payload
651
 *
652
 * Add a hash table entry with three strings as key.
653
 *
654
 * See xmlHashAddEntry.
655
 *
656
 * Returns 0 on success and -1 in case of error.
657
 */
658
int
659
xmlHashAddEntry3(xmlHashTablePtr hash, const xmlChar *key,
660
                 const xmlChar *key2, const xmlChar *key3,
661
0
                 void *payload) {
662
0
    return(xmlHashUpdateInternal(hash, key, key2, key3, payload, NULL, 0));
663
0
}
664
665
/**
666
 * xmlHashUpdateEntry:
667
 * @hash: hash table
668
 * @key: string key
669
 * @payload: pointer to the payload
670
 * @dealloc: deallocator function for replaced item or NULL
671
 *
672
 * Add a hash table entry. If an entry with this key already exists,
673
 * the old payload will be freed and updated with the new value.
674
 *
675
 * Returns 0 in case of success, -1 if a memory allocation failed.
676
 */
677
int
678
xmlHashUpdateEntry(xmlHashTablePtr hash, const xmlChar *key,
679
0
                   void *payload, xmlHashDeallocator dealloc) {
680
0
    return(xmlHashUpdateInternal(hash, key, NULL, NULL, payload,
681
0
                                 dealloc, 1));
682
0
}
683
684
/**
685
 * xmlHashUpdateEntry2:
686
 * @hash: hash table
687
 * @key: first string key
688
 * @key2: second string key
689
 * @payload: pointer to the payload
690
 * @dealloc: deallocator function for replaced item or NULL
691
 *
692
 * Add a hash table entry with two strings as key.
693
 *
694
 * See xmlHashUpdateEntry.
695
 *
696
 * Returns 0 on success and -1 in case of error.
697
 */
698
int
699
xmlHashUpdateEntry2(xmlHashTablePtr hash, const xmlChar *key,
700
                   const xmlChar *key2, void *payload,
701
0
                   xmlHashDeallocator dealloc) {
702
0
    return(xmlHashUpdateInternal(hash, key, key2, NULL, payload,
703
0
                                 dealloc, 1));
704
0
}
705
706
/**
707
 * xmlHashUpdateEntry3:
708
 * @hash: hash table
709
 * @key: first string key
710
 * @key2: second string key
711
 * @key3: third string key
712
 * @payload: pointer to the payload
713
 * @dealloc: deallocator function for replaced item or NULL
714
 *
715
 * Add a hash table entry with three strings as key.
716
 *
717
 * See xmlHashUpdateEntry.
718
 *
719
 * Returns 0 on success and -1 in case of error.
720
 */
721
int
722
xmlHashUpdateEntry3(xmlHashTablePtr hash, const xmlChar *key,
723
                   const xmlChar *key2, const xmlChar *key3,
724
0
                   void *payload, xmlHashDeallocator dealloc) {
725
0
    return(xmlHashUpdateInternal(hash, key, key2, key3, payload,
726
0
                                 dealloc, 1));
727
0
}
728
729
/**
730
 * xmlHashLookup:
731
 * @hash: hash table
732
 * @key: string key
733
 *
734
 * Find the entry specified by @key.
735
 *
736
 * Returns a pointer to the payload or NULL if no entry was found.
737
 */
738
void *
739
0
xmlHashLookup(xmlHashTablePtr hash, const xmlChar *key) {
740
0
    return(xmlHashLookup3(hash, key, NULL, NULL));
741
0
}
742
743
/**
744
 * xmlHashLookup2:
745
 * @hash: hash table
746
 * @key: first string key
747
 * @key2: second string key
748
 *
749
 * Find the payload specified by the (@key, @key2) tuple.
750
 *
751
 * Returns a pointer to the payload or NULL if no entry was found.
752
 */
753
void *
754
xmlHashLookup2(xmlHashTablePtr hash, const xmlChar *key,
755
0
              const xmlChar *key2) {
756
0
    return(xmlHashLookup3(hash, key, key2, NULL));
757
0
}
758
759
/**
760
 * xmlHashQLookup:
761
 * @hash: hash table
762
 * @prefix: prefix of the string key
763
 * @name: local name of the string key
764
 *
765
 * Find the payload specified by the QName @prefix:@name or @name.
766
 *
767
 * Returns a pointer to the payload or NULL if no entry was found.
768
 */
769
void *
770
xmlHashQLookup(xmlHashTablePtr hash, const xmlChar *prefix,
771
0
               const xmlChar *name) {
772
0
    return(xmlHashQLookup3(hash, prefix, name, NULL, NULL, NULL, NULL));
773
0
}
774
775
/**
776
 * xmlHashQLookup2:
777
 * @hash: hash table
778
 * @prefix: first prefix
779
 * @name: first local name
780
 * @prefix2: second prefix
781
 * @name2: second local name
782
 *
783
 * Find the payload specified by the QNames tuple.
784
 *
785
 * Returns a pointer to the payload or NULL if no entry was found.
786
 */
787
void *
788
xmlHashQLookup2(xmlHashTablePtr hash, const xmlChar *prefix,
789
                const xmlChar *name, const xmlChar *prefix2,
790
0
                const xmlChar *name2) {
791
0
    return(xmlHashQLookup3(hash, prefix, name, prefix2, name2, NULL, NULL));
792
0
}
793
794
/**
795
 * xmlHashLookup3:
796
 * @hash: hash table
797
 * @key: first string key
798
 * @key2: second string key
799
 * @key3: third string key
800
 *
801
 * Find the payload specified by the (@key, @key2, @key3) tuple.
802
 *
803
 * Returns a pointer to the payload or NULL if no entry was found.
804
 */
805
void *
806
xmlHashLookup3(xmlHashTablePtr hash, const xmlChar *key,
807
0
               const xmlChar *key2, const xmlChar *key3) {
808
0
    const xmlHashEntry *entry;
809
0
    unsigned hashValue;
810
0
    int found;
811
812
0
    if ((hash == NULL) || (hash->size == 0) || (key == NULL))
813
0
        return(NULL);
814
0
    hashValue = xmlHashValue(hash->randomSeed, key, key2, key3, NULL);
815
0
    entry = xmlHashFindEntry(hash, key, key2, key3, hashValue, &found);
816
0
    if (found)
817
0
        return(entry->payload);
818
0
    return(NULL);
819
0
}
820
821
/**
822
 * xmlHashQLookup3:
823
 * @hash: hash table
824
 * @prefix: first prefix
825
 * @name: first local name
826
 * @prefix2: second prefix
827
 * @name2: second local name
828
 * @prefix3: third prefix
829
 * @name3: third local name
830
 *
831
 * Find the payload specified by the QNames tuple.
832
 *
833
 * Returns a pointer to the payload or NULL if no entry was found.
834
 */
835
ATTRIBUTE_NO_SANITIZE_INTEGER
836
void *
837
xmlHashQLookup3(xmlHashTablePtr hash,
838
                const xmlChar *prefix, const xmlChar *name,
839
                const xmlChar *prefix2, const xmlChar *name2,
840
0
                const xmlChar *prefix3, const xmlChar *name3) {
841
0
    const xmlHashEntry *entry;
842
0
    unsigned hashValue, mask, pos, displ;
843
844
0
    if ((hash == NULL) || (hash->size == 0) || (name == NULL))
845
0
        return(NULL);
846
847
0
    hashValue = xmlHashQNameValue(hash->randomSeed, prefix, name, prefix2,
848
0
                                  name2, prefix3, name3);
849
0
    mask = hash->size - 1;
850
0
    pos = hashValue & mask;
851
0
    entry = &hash->table[pos];
852
853
0
    if (entry->hashValue != 0) {
854
0
        displ = 0;
855
0
        hashValue |= MAX_HASH_SIZE;
856
857
0
        do {
858
0
            if ((hashValue == entry->hashValue) &&
859
0
                (xmlStrQEqual(prefix, name, entry->key)) &&
860
0
                (xmlStrQEqual(prefix2, name2, entry->key2)) &&
861
0
                (xmlStrQEqual(prefix3, name3, entry->key3)))
862
0
                return(entry->payload);
863
864
0
            displ++;
865
0
            pos++;
866
0
            entry++;
867
0
            if ((pos & mask) == 0)
868
0
                entry = hash->table;
869
0
        } while ((entry->hashValue != 0) &&
870
0
                 (((pos - entry->hashValue) & mask) >= displ));
871
0
    }
872
873
0
    return(NULL);
874
0
}
875
876
typedef struct {
877
    xmlHashScanner scan;
878
    void *data;
879
} stubData;
880
881
static void
882
stubHashScannerFull(void *payload, void *data, const xmlChar *key,
883
                    const xmlChar *key2 ATTRIBUTE_UNUSED,
884
0
                    const xmlChar *key3 ATTRIBUTE_UNUSED) {
885
0
    stubData *sdata = (stubData *) data;
886
0
    sdata->scan(payload, sdata->data, key);
887
0
}
888
889
/**
890
 * xmlHashScan:
891
 * @hash: hash table
892
 * @scan: scanner function for items in the hash
893
 * @data: extra data passed to @scan
894
 *
895
 * Scan the hash @table and apply @scan to each value.
896
 */
897
void
898
0
xmlHashScan(xmlHashTablePtr hash, xmlHashScanner scan, void *data) {
899
0
    stubData sdata;
900
0
    sdata.data = data;
901
0
    sdata.scan = scan;
902
0
    xmlHashScanFull(hash, stubHashScannerFull, &sdata);
903
0
}
904
905
/**
906
 * xmlHashScanFull:
907
 * @hash: hash table
908
 * @scan: scanner function for items in the hash
909
 * @data: extra data passed to @scan
910
 *
911
 * Scan the hash @table and apply @scan to each value.
912
 */
913
void
914
0
xmlHashScanFull(xmlHashTablePtr hash, xmlHashScannerFull scan, void *data) {
915
0
    const xmlHashEntry *entry, *end;
916
0
    xmlHashEntry old;
917
0
    unsigned i;
918
919
0
    if ((hash == NULL) || (hash->size == 0) || (scan == NULL))
920
0
        return;
921
922
    /*
923
     * We must handle the case that a scanned entry is removed when executing
924
     * the callback (xmlCleanSpecialAttr and possibly other places).
925
     *
926
     * Find the start of a probe sequence to avoid scanning entries twice if
927
     * a deletion happens.
928
     */
929
0
    entry = hash->table;
930
0
    end = &hash->table[hash->size];
931
0
    while (entry->hashValue != 0) {
932
0
        if (++entry >= end)
933
0
            entry = hash->table;
934
0
    }
935
936
0
    for (i = 0; i < hash->size; i++) {
937
0
        if ((entry->hashValue != 0) && (entry->payload != NULL)) {
938
            /*
939
             * Make sure to rescan after a possible deletion.
940
             */
941
0
            do {
942
0
                old = *entry;
943
0
                scan(entry->payload, data, entry->key, entry->key2, entry->key3);
944
0
            } while ((entry->hashValue != 0) &&
945
0
                     (entry->payload != NULL) &&
946
0
                     ((entry->key != old.key) ||
947
0
                      (entry->key2 != old.key2) ||
948
0
                      (entry->key3 != old.key3)));
949
0
        }
950
0
        if (++entry >= end)
951
0
            entry = hash->table;
952
0
    }
953
0
}
954
955
/**
956
 * xmlHashScan3:
957
 * @hash: hash table
958
 * @key: first string key or NULL
959
 * @key2: second string key or NULL
960
 * @key3: third string key or NULL
961
 * @scan: scanner function for items in the hash
962
 * @data: extra data passed to @scan
963
 *
964
 * Scan the hash @table and apply @scan to each value matching
965
 * (@key, @key2, @key3) tuple. If one of the keys is null,
966
 * the comparison is considered to match.
967
 */
968
void
969
xmlHashScan3(xmlHashTablePtr hash, const xmlChar *key,
970
             const xmlChar *key2, const xmlChar *key3,
971
0
             xmlHashScanner scan, void *data) {
972
0
    stubData sdata;
973
0
    sdata.data = data;
974
0
    sdata.scan = scan;
975
0
    xmlHashScanFull3(hash, key, key2, key3, stubHashScannerFull, &sdata);
976
0
}
977
978
/**
979
 * xmlHashScanFull3:
980
 * @hash: hash table
981
 * @key: first string key or NULL
982
 * @key2: second string key or NULL
983
 * @key3: third string key or NULL
984
 * @scan: scanner function for items in the hash
985
 * @data: extra data passed to @scan
986
 *
987
 * Scan the hash @table and apply @scan to each value matching
988
 * (@key, @key2, @key3) tuple. If one of the keys is null,
989
 * the comparison is considered to match.
990
 */
991
void
992
xmlHashScanFull3(xmlHashTablePtr hash, const xmlChar *key,
993
                 const xmlChar *key2, const xmlChar *key3,
994
0
                 xmlHashScannerFull scan, void *data) {
995
0
    const xmlHashEntry *entry, *end;
996
0
    xmlHashEntry old;
997
0
    unsigned i;
998
999
0
    if ((hash == NULL) || (hash->size == 0) || (scan == NULL))
1000
0
        return;
1001
1002
    /*
1003
     * We must handle the case that a scanned entry is removed when executing
1004
     * the callback (xmlCleanSpecialAttr and possibly other places).
1005
     *
1006
     * Find the start of a probe sequence to avoid scanning entries twice if
1007
     * a deletion happens.
1008
     */
1009
0
    entry = hash->table;
1010
0
    end = &hash->table[hash->size];
1011
0
    while (entry->hashValue != 0) {
1012
0
        if (++entry >= end)
1013
0
            entry = hash->table;
1014
0
    }
1015
1016
0
    for (i = 0; i < hash->size; i++) {
1017
0
        if ((entry->hashValue != 0) && (entry->payload != NULL)) {
1018
            /*
1019
             * Make sure to rescan after a possible deletion.
1020
             */
1021
0
            do {
1022
0
                if (((key != NULL) && (strcmp((const char *) key,
1023
0
                                              (const char *) entry->key) != 0)) ||
1024
0
                    ((key2 != NULL) && (!xmlFastStrEqual(key2, entry->key2))) ||
1025
0
                    ((key3 != NULL) && (!xmlFastStrEqual(key3, entry->key3))))
1026
0
                    break;
1027
0
                old = *entry;
1028
0
                scan(entry->payload, data, entry->key, entry->key2, entry->key3);
1029
0
            } while ((entry->hashValue != 0) &&
1030
0
                     (entry->payload != NULL) &&
1031
0
                     ((entry->key != old.key) ||
1032
0
                      (entry->key2 != old.key2) ||
1033
0
                      (entry->key3 != old.key3)));
1034
0
        }
1035
0
        if (++entry >= end)
1036
0
            entry = hash->table;
1037
0
    }
1038
0
}
1039
1040
/**
1041
 * xmlHashCopy:
1042
 * @hash: hash table
1043
 * @copy: copier function for items in the hash
1044
 *
1045
 * Copy the hash @table using @copy to copy payloads.
1046
 *
1047
 * Returns the new table or NULL if a memory allocation failed.
1048
 */
1049
xmlHashTablePtr
1050
0
xmlHashCopy(xmlHashTablePtr hash, xmlHashCopier copy) {
1051
0
    const xmlHashEntry *entry, *end;
1052
0
    xmlHashTablePtr ret;
1053
1054
0
    if ((hash == NULL) || (copy == NULL))
1055
0
        return(NULL);
1056
1057
0
    ret = xmlHashCreate(hash->size);
1058
0
    if (ret == NULL)
1059
0
        return(NULL);
1060
1061
0
    if (hash->size == 0)
1062
0
        return(ret);
1063
1064
0
    end = &hash->table[hash->size];
1065
1066
0
    for (entry = hash->table; entry < end; entry++) {
1067
0
        if (entry->hashValue != 0)
1068
0
            xmlHashAddEntry3(ret, entry->key, entry->key2, entry->key3,
1069
0
                             copy(entry->payload, entry->key));
1070
0
    }
1071
1072
0
    return(ret);
1073
0
}
1074
1075
/**
1076
 * xmlHashSize:
1077
 * @hash: hash table
1078
 *
1079
 * Query the number of elements in the hash table.
1080
 *
1081
 * Returns the number of elements in the hash table or
1082
 * -1 in case of error.
1083
 */
1084
int
1085
0
xmlHashSize(xmlHashTablePtr hash) {
1086
0
    if (hash == NULL)
1087
0
        return(-1);
1088
0
    return(hash->nbElems);
1089
0
}
1090
1091
/**
1092
 * xmlHashRemoveEntry:
1093
 * @hash: hash table
1094
 * @key: string key
1095
 * @dealloc: deallocator function for removed item or NULL
1096
 *
1097
 * Find the entry specified by the @key and remove it from the hash table.
1098
 * Payload will be freed with @dealloc.
1099
 *
1100
 * Returns 0 on success and -1 if no entry was found.
1101
 */
1102
int xmlHashRemoveEntry(xmlHashTablePtr hash, const xmlChar *key,
1103
0
                       xmlHashDeallocator dealloc) {
1104
0
    return(xmlHashRemoveEntry3(hash, key, NULL, NULL, dealloc));
1105
0
}
1106
1107
/**
1108
 * xmlHashRemoveEntry2:
1109
 * @hash: hash table
1110
 * @key: first string key
1111
 * @key2: second string key
1112
 * @dealloc: deallocator function for removed item or NULL
1113
 *
1114
 * Remove an entry with two strings as key.
1115
 *
1116
 * See xmlHashRemoveEntry.
1117
 *
1118
 * Returns 0 on success and -1 in case of error.
1119
 */
1120
int
1121
xmlHashRemoveEntry2(xmlHashTablePtr hash, const xmlChar *key,
1122
0
                    const xmlChar *key2, xmlHashDeallocator dealloc) {
1123
0
    return(xmlHashRemoveEntry3(hash, key, key2, NULL, dealloc));
1124
0
}
1125
1126
/**
1127
 * xmlHashRemoveEntry3:
1128
 * @hash: hash table
1129
 * @key: first string key
1130
 * @key2: second string key
1131
 * @key3: third string key
1132
 * @dealloc: deallocator function for removed item or NULL
1133
 *
1134
 * Remove an entry with three strings as key.
1135
 *
1136
 * See xmlHashRemoveEntry.
1137
 *
1138
 * Returns 0 on success and -1 in case of error.
1139
 */
1140
ATTRIBUTE_NO_SANITIZE_INTEGER
1141
int
1142
xmlHashRemoveEntry3(xmlHashTablePtr hash, const xmlChar *key,
1143
                    const xmlChar *key2, const xmlChar *key3,
1144
0
                    xmlHashDeallocator dealloc) {
1145
0
    xmlHashEntry *entry, *cur, *next;
1146
0
    unsigned hashValue, mask, pos, nextpos;
1147
0
    int found;
1148
1149
0
    if ((hash == NULL) || (hash->size == 0) || (key == NULL))
1150
0
        return(-1);
1151
1152
0
    hashValue = xmlHashValue(hash->randomSeed, key, key2, key3, NULL);
1153
0
    entry = xmlHashFindEntry(hash, key, key2, key3, hashValue, &found);
1154
0
    if (!found)
1155
0
        return(-1);
1156
1157
0
    if ((dealloc != NULL) && (entry->payload != NULL))
1158
0
        dealloc(entry->payload, entry->key);
1159
0
    if (hash->dict == NULL) {
1160
0
        if (entry->key)
1161
0
            xmlFree(entry->key);
1162
0
        if (entry->key2)
1163
0
            xmlFree(entry->key2);
1164
0
        if (entry->key3)
1165
0
            xmlFree(entry->key3);
1166
0
    }
1167
1168
    /*
1169
     * Find end of probe sequence. Entries at their initial probe
1170
     * position start a new sequence.
1171
     */
1172
0
    mask = hash->size - 1;
1173
0
    pos = entry - hash->table;
1174
0
    cur = entry;
1175
1176
0
    while (1) {
1177
0
        nextpos = pos + 1;
1178
0
        next = cur + 1;
1179
0
        if ((nextpos & mask) == 0)
1180
0
            next = hash->table;
1181
1182
0
        if ((next->hashValue == 0) ||
1183
0
            (((next->hashValue - nextpos) & mask) == 0))
1184
0
            break;
1185
1186
0
        cur = next;
1187
0
        pos = nextpos;
1188
0
    }
1189
1190
    /*
1191
     * Backward shift
1192
     */
1193
0
    next = entry + 1;
1194
1195
0
    if (cur < entry) {
1196
0
        xmlHashEntry *end = &hash->table[hash->size];
1197
1198
0
        memmove(entry, next, (char *) end - (char *) next);
1199
0
        entry = hash->table;
1200
0
        end[-1] = *entry;
1201
0
        next = entry + 1;
1202
0
    }
1203
1204
0
    memmove(entry, next, (char *) cur - (char *) entry);
1205
1206
    /*
1207
     * Update entry
1208
     */
1209
0
    cur->hashValue = 0;
1210
1211
0
    hash->nbElems--;
1212
1213
0
    return(0);
1214
0
}
1215