Coverage Report

Created: 2025-06-24 07:01

/src/ghostpdl/tiff/libtiff/tif_hash_set.c
Line
Count
Source (jump to first uncovered line)
1
/**********************************************************************
2
 *
3
 * Name:     tif_hash_set.c
4
 * Purpose:  Hash set functions.
5
 * Author:   Even Rouault, <even dot rouault at spatialys.com>
6
 *
7
 **********************************************************************
8
 * Copyright (c) 2008-2009, Even Rouault <even dot rouault at spatialys.com>
9
 *
10
 * Permission is hereby granted, free of charge, to any person obtaining a
11
 * copy of this software and associated documentation files (the "Software"),
12
 * to deal in the Software without restriction, including without limitation
13
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14
 * and/or sell copies of the Software, and to permit persons to whom the
15
 * Software is furnished to do so, subject to the following conditions:
16
 *
17
 * The above copyright notice and this permission notice shall be included
18
 * in all copies or substantial portions of the Software.
19
 *
20
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
23
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26
 * DEALINGS IN THE SOFTWARE.
27
 ****************************************************************************/
28
29
#include "tif_config.h"
30
31
#include "tif_hash_set.h"
32
33
#include <assert.h>
34
#define GS_TIFF_BUILD
35
#ifndef GS_TIFF_BUILD
36
#include <stdbool.h>
37
#include <stdint.h>
38
#endif
39
#include <stdio.h>
40
#include <stdlib.h>
41
42
/** List element structure. */
43
typedef struct _TIFFList TIFFList;
44
45
/** List element structure. */
46
struct _TIFFList
47
{
48
    /*! Pointer to the data object. Should be allocated and freed by the
49
     * caller.
50
     * */
51
    void *pData;
52
    /*! Pointer to the next element in list. NULL, if current element is the
53
     * last one.
54
     */
55
    struct _TIFFList *psNext;
56
};
57
58
struct _TIFFHashSet
59
{
60
    TIFFHashSetHashFunc fnHashFunc;
61
    TIFFHashSetEqualFunc fnEqualFunc;
62
    TIFFHashSetFreeEltFunc fnFreeEltFunc;
63
    TIFFList **tabList;
64
    int nSize;
65
    int nIndiceAllocatedSize;
66
    int nAllocatedSize;
67
    TIFFList *psRecyclingList;
68
    int nRecyclingListSize;
69
    bool bRehash;
70
#ifdef HASH_DEBUG
71
    int nCollisions;
72
#endif
73
};
74
75
static const int anPrimes[] = {
76
    53,        97,        193,       389,       769,       1543,     3079,
77
    6151,      12289,     24593,     49157,     98317,     196613,   393241,
78
    786433,    1572869,   3145739,   6291469,   12582917,  25165843, 50331653,
79
    100663319, 201326611, 402653189, 805306457, 1610612741};
80
81
/************************************************************************/
82
/*                    TIFFHashSetHashPointer()                          */
83
/************************************************************************/
84
85
/**
86
 * Hash function for an arbitrary pointer
87
 *
88
 * @param elt the arbitrary pointer to hash
89
 *
90
 * @return the hash value of the pointer
91
 */
92
93
static unsigned long TIFFHashSetHashPointer(const void *elt)
94
0
{
95
0
    return (unsigned long)(uintptr_t)((void *)(elt));
96
0
}
97
98
/************************************************************************/
99
/*                   TIFFHashSetEqualPointer()                          */
100
/************************************************************************/
101
102
/**
103
 * Equality function for arbitrary pointers
104
 *
105
 * @param elt1 the first arbitrary pointer to compare
106
 * @param elt2 the second arbitrary pointer to compare
107
 *
108
 * @return true if the pointers are equal
109
 */
110
111
static bool TIFFHashSetEqualPointer(const void *elt1, const void *elt2)
112
0
{
113
0
    return elt1 == elt2;
114
0
}
115
116
/************************************************************************/
117
/*                          TIFFHashSetNew()                             */
118
/************************************************************************/
119
120
/**
121
 * Creates a new hash set
122
 *
123
 * The hash function must return a hash value for the elements to insert.
124
 * If fnHashFunc is NULL, TIFFHashSetHashPointer will be used.
125
 *
126
 * The equal function must return if two elements are equal.
127
 * If fnEqualFunc is NULL, TIFFHashSetEqualPointer will be used.
128
 *
129
 * The free function is used to free elements inserted in the hash set,
130
 * when the hash set is destroyed, when elements are removed or replaced.
131
 * If fnFreeEltFunc is NULL, elements inserted into the hash set will not be
132
 * freed.
133
 *
134
 * @param fnHashFunc hash function. May be NULL.
135
 * @param fnEqualFunc equal function. May be NULL.
136
 * @param fnFreeEltFunc element free function. May be NULL.
137
 *
138
 * @return a new hash set
139
 */
140
141
TIFFHashSet *TIFFHashSetNew(TIFFHashSetHashFunc fnHashFunc,
142
                            TIFFHashSetEqualFunc fnEqualFunc,
143
                            TIFFHashSetFreeEltFunc fnFreeEltFunc)
144
37.6k
{
145
37.6k
    TIFFHashSet *set = (TIFFHashSet *)malloc(sizeof(TIFFHashSet));
146
37.6k
    if (set == NULL)
147
0
        return NULL;
148
37.6k
    set->fnHashFunc = fnHashFunc ? fnHashFunc : TIFFHashSetHashPointer;
149
37.6k
    set->fnEqualFunc = fnEqualFunc ? fnEqualFunc : TIFFHashSetEqualPointer;
150
37.6k
    set->fnFreeEltFunc = fnFreeEltFunc;
151
37.6k
    set->nSize = 0;
152
37.6k
    set->tabList = (TIFFList **)(calloc(53, sizeof(TIFFList *)));
153
37.6k
    if (set->tabList == NULL)
154
0
    {
155
0
        free(set);
156
0
        return NULL;
157
0
    }
158
37.6k
    set->nIndiceAllocatedSize = 0;
159
37.6k
    set->nAllocatedSize = 53;
160
37.6k
    set->psRecyclingList = NULL;
161
37.6k
    set->nRecyclingListSize = 0;
162
37.6k
    set->bRehash = false;
163
#ifdef HASH_DEBUG
164
    set->nCollisions = 0;
165
#endif
166
37.6k
    return set;
167
37.6k
}
168
169
/************************************************************************/
170
/*                          TIFFHashSetSize()                            */
171
/************************************************************************/
172
173
/**
174
 * Returns the number of elements inserted in the hash set
175
 *
176
 * Note: this is not the internal size of the hash set
177
 *
178
 * @param set the hash set
179
 *
180
 * @return the number of elements in the hash set
181
 */
182
183
int TIFFHashSetSize(const TIFFHashSet *set)
184
18.8k
{
185
18.8k
    assert(set != NULL);
186
18.8k
    return set->nSize;
187
18.8k
}
188
189
/************************************************************************/
190
/*                       TIFFHashSetGetNewListElt()                      */
191
/************************************************************************/
192
193
static TIFFList *TIFFHashSetGetNewListElt(TIFFHashSet *set)
194
37.6k
{
195
37.6k
    if (set->psRecyclingList)
196
4
    {
197
4
        TIFFList *psRet = set->psRecyclingList;
198
4
        psRet->pData = NULL;
199
4
        set->nRecyclingListSize--;
200
4
        set->psRecyclingList = psRet->psNext;
201
4
        return psRet;
202
4
    }
203
204
37.6k
    return (TIFFList *)malloc(sizeof(TIFFList));
205
37.6k
}
206
207
/************************************************************************/
208
/*                       TIFFHashSetReturnListElt()                      */
209
/************************************************************************/
210
211
static void TIFFHashSetReturnListElt(TIFFHashSet *set, TIFFList *psList)
212
4
{
213
4
    if (set->nRecyclingListSize < 128)
214
4
    {
215
4
        psList->psNext = set->psRecyclingList;
216
4
        set->psRecyclingList = psList;
217
4
        set->nRecyclingListSize++;
218
4
    }
219
0
    else
220
0
    {
221
0
        free(psList);
222
0
    }
223
4
}
224
225
/************************************************************************/
226
/*                   TIFFHashSetClearInternal()                          */
227
/************************************************************************/
228
229
static void TIFFHashSetClearInternal(TIFFHashSet *set, bool bFinalize)
230
37.6k
{
231
37.6k
    int i;
232
37.6k
    assert(set != NULL);
233
2.03M
    for (i = 0; i < set->nAllocatedSize; i++)
234
1.99M
    {
235
1.99M
        TIFFList *cur = set->tabList[i];
236
2.03M
        while (cur)
237
37.6k
        {
238
37.6k
            TIFFList *psNext;
239
37.6k
            if (set->fnFreeEltFunc)
240
18.8k
                set->fnFreeEltFunc(cur->pData);
241
37.6k
            psNext = cur->psNext;
242
37.6k
            if (bFinalize)
243
37.6k
                free(cur);
244
0
            else
245
0
                TIFFHashSetReturnListElt(set, cur);
246
37.6k
            cur = psNext;
247
37.6k
        }
248
1.99M
        set->tabList[i] = NULL;
249
1.99M
    }
250
37.6k
    set->bRehash = false;
251
37.6k
}
252
253
/************************************************************************/
254
/*                         TIFFListDestroy()                            */
255
/************************************************************************/
256
257
/**
258
 * Destroy a list. Caller responsible for freeing data objects contained in
259
 * list elements.
260
 *
261
 * @param psList pointer to list head.
262
 *
263
 */
264
265
static void TIFFListDestroy(TIFFList *psList)
266
37.6k
{
267
37.6k
    TIFFList *psCurrent = psList;
268
269
37.6k
    while (psCurrent)
270
0
    {
271
0
        TIFFList *const psNext = psCurrent->psNext;
272
0
        free(psCurrent);
273
0
        psCurrent = psNext;
274
0
    }
275
37.6k
}
276
277
/************************************************************************/
278
/*                        TIFFHashSetDestroy()                          */
279
/************************************************************************/
280
281
/**
282
 * Destroys an allocated hash set.
283
 *
284
 * This function also frees the elements if a free function was
285
 * provided at the creation of the hash set.
286
 *
287
 * @param set the hash set
288
 */
289
290
void TIFFHashSetDestroy(TIFFHashSet *set)
291
37.6k
{
292
37.6k
    if (set)
293
37.6k
    {
294
37.6k
        TIFFHashSetClearInternal(set, true);
295
37.6k
        free(set->tabList);
296
37.6k
        TIFFListDestroy(set->psRecyclingList);
297
37.6k
        free(set);
298
37.6k
    }
299
37.6k
}
300
301
#ifdef notused
302
/************************************************************************/
303
/*                        TIFFHashSetClear()                             */
304
/************************************************************************/
305
306
/**
307
 * Clear all elements from a hash set.
308
 *
309
 * This function also frees the elements if a free function was
310
 * provided at the creation of the hash set.
311
 *
312
 * @param set the hash set
313
 */
314
315
void TIFFHashSetClear(TIFFHashSet *set)
316
{
317
    TIFFHashSetClearInternal(set, false);
318
    set->nIndiceAllocatedSize = 0;
319
    set->nAllocatedSize = 53;
320
#ifdef HASH_DEBUG
321
    set->nCollisions = 0;
322
#endif
323
    set->nSize = 0;
324
}
325
326
/************************************************************************/
327
/*                       TIFFHashSetForeach()                           */
328
/************************************************************************/
329
330
/**
331
 * Walk through the hash set and runs the provided function on all the
332
 * elements
333
 *
334
 * This function is provided the user_data argument of TIFFHashSetForeach.
335
 * It must return true to go on the walk through the hash set, or FALSE to
336
 * make it stop.
337
 *
338
 * Note : the structure of the hash set must *NOT* be modified during the
339
 * walk.
340
 *
341
 * @param set the hash set.
342
 * @param fnIterFunc the function called on each element.
343
 * @param user_data the user data provided to the function.
344
 */
345
346
void TIFFHashSetForeach(TIFFHashSet *set, TIFFHashSetIterEltFunc fnIterFunc,
347
                        void *user_data)
348
{
349
    assert(set != NULL);
350
    if (!fnIterFunc)
351
        return;
352
353
    for (int i = 0; i < set->nAllocatedSize; i++)
354
    {
355
        TIFFList *cur = set->tabList[i];
356
        while (cur)
357
        {
358
            if (!fnIterFunc(cur->pData, user_data))
359
                return;
360
361
            cur = cur->psNext;
362
        }
363
    }
364
}
365
#endif
366
367
/************************************************************************/
368
/*                        TIFFHashSetRehash()                           */
369
/************************************************************************/
370
371
static bool TIFFHashSetRehash(TIFFHashSet *set)
372
0
{
373
0
    int nNewAllocatedSize = anPrimes[set->nIndiceAllocatedSize];
374
0
    int i;
375
0
    TIFFList **newTabList =
376
0
        (TIFFList **)(calloc(nNewAllocatedSize, sizeof(TIFFList *)));
377
0
    if (newTabList == NULL)
378
0
        return false;
379
#ifdef HASH_DEBUG
380
    TIFFDebug("TIFFHASH",
381
              "hashSet=%p, nSize=%d, nCollisions=%d, "
382
              "fCollisionRate=%.02f",
383
              set, set->nSize, set->nCollisions,
384
              set->nCollisions * 100.0 / set->nSize);
385
    set->nCollisions = 0;
386
#endif
387
0
    for (i = 0; i < set->nAllocatedSize; i++)
388
0
    {
389
0
        TIFFList *cur = set->tabList[i];
390
0
        while (cur)
391
0
        {
392
0
            const unsigned long nNewHashVal =
393
0
                set->fnHashFunc(cur->pData) % nNewAllocatedSize;
394
#ifdef HASH_DEBUG
395
            if (newTabList[nNewHashVal])
396
                set->nCollisions++;
397
#endif
398
0
            TIFFList *psNext = cur->psNext;
399
0
            cur->psNext = newTabList[nNewHashVal];
400
0
            newTabList[nNewHashVal] = cur;
401
0
            cur = psNext;
402
0
        }
403
0
    }
404
0
    free(set->tabList);
405
0
    set->tabList = newTabList;
406
0
    set->nAllocatedSize = nNewAllocatedSize;
407
0
    set->bRehash = false;
408
0
    return true;
409
0
}
410
411
/************************************************************************/
412
/*                        TIFFHashSetFindPtr()                          */
413
/************************************************************************/
414
415
static void **TIFFHashSetFindPtr(TIFFHashSet *set, const void *elt)
416
94.0k
{
417
94.0k
    const unsigned long nHashVal = set->fnHashFunc(elt) % set->nAllocatedSize;
418
94.0k
    TIFFList *cur = set->tabList[nHashVal];
419
94.0k
    while (cur)
420
18.8k
    {
421
18.8k
        if (set->fnEqualFunc(cur->pData, elt))
422
18.8k
            return &cur->pData;
423
0
        cur = cur->psNext;
424
0
    }
425
75.2k
    return NULL;
426
94.0k
}
427
428
/************************************************************************/
429
/*                         TIFFHashSetInsert()                          */
430
/************************************************************************/
431
432
/**
433
 * Inserts an element into a hash set.
434
 *
435
 * If the element was already inserted in the hash set, the previous
436
 * element is replaced by the new element. If a free function was provided,
437
 * it is used to free the previously inserted element
438
 *
439
 * @param set the hash set
440
 * @param elt the new element to insert in the hash set
441
 *
442
 * @return true if success. If false is returned, elt has not been inserted,
443
 * but TIFFHashSetInsert() will have run the free function if provided.
444
 */
445
446
bool TIFFHashSetInsert(TIFFHashSet *set, void *elt)
447
37.6k
{
448
37.6k
    void **pElt;
449
37.6k
    assert(set != NULL);
450
37.6k
    pElt = TIFFHashSetFindPtr(set, elt);
451
37.6k
    if (pElt)
452
0
    {
453
0
        if (set->fnFreeEltFunc)
454
0
            set->fnFreeEltFunc(*pElt);
455
456
0
        *pElt = elt;
457
0
        return true;
458
0
    }
459
460
37.6k
    if (set->nSize >= 2 * set->nAllocatedSize / 3 ||
461
37.6k
        (set->bRehash && set->nIndiceAllocatedSize > 0 &&
462
37.6k
         set->nSize <= set->nAllocatedSize / 2))
463
0
    {
464
0
        set->nIndiceAllocatedSize++;
465
0
        if (!TIFFHashSetRehash(set))
466
0
        {
467
0
            set->nIndiceAllocatedSize--;
468
0
            if (set->fnFreeEltFunc)
469
0
                set->fnFreeEltFunc(elt);
470
0
            return false;
471
0
        }
472
0
    }
473
474
37.6k
    {
475
37.6k
        const unsigned long nHashVal = set->fnHashFunc(elt) % set->nAllocatedSize;
476
#ifdef HASH_DEBUG
477
        if (set->tabList[nHashVal])
478
            set->nCollisions++;
479
#endif
480
481
37.6k
        TIFFList *new_elt = TIFFHashSetGetNewListElt(set);
482
37.6k
        if (new_elt == NULL)
483
0
        {
484
0
            if (set->fnFreeEltFunc)
485
0
                set->fnFreeEltFunc(elt);
486
0
            return false;
487
0
        }
488
37.6k
        new_elt->pData = elt;
489
37.6k
        new_elt->psNext = set->tabList[nHashVal];
490
37.6k
        set->tabList[nHashVal] = new_elt;
491
37.6k
        set->nSize++;
492
37.6k
    }
493
37.6k
    return true;
494
37.6k
}
495
496
/************************************************************************/
497
/*                        TIFFHashSetLookup()                           */
498
/************************************************************************/
499
500
/**
501
 * Returns the element found in the hash set corresponding to the element to
502
 * look up The element must not be modified.
503
 *
504
 * @param set the hash set
505
 * @param elt the element to look up in the hash set
506
 *
507
 * @return the element found in the hash set or NULL
508
 */
509
510
void *TIFFHashSetLookup(TIFFHashSet *set, const void *elt)
511
56.4k
{
512
56.4k
    void **pElt;
513
56.4k
    assert(set != NULL);
514
56.4k
    pElt = TIFFHashSetFindPtr(set, elt);
515
56.4k
    if (pElt)
516
18.8k
        return *pElt;
517
518
37.6k
    return NULL;
519
56.4k
}
520
521
/************************************************************************/
522
/*                     TIFFHashSetRemoveInternal()                      */
523
/************************************************************************/
524
525
static bool TIFFHashSetRemoveInternal(TIFFHashSet *set, const void *elt,
526
                                      bool bDeferRehash)
527
4
{
528
4
    int nHashVal;
529
4
    TIFFList *cur, *prev;
530
4
    assert(set != NULL);
531
4
    if (set->nIndiceAllocatedSize > 0 && set->nSize <= set->nAllocatedSize / 2)
532
0
    {
533
0
        set->nIndiceAllocatedSize--;
534
0
        if (bDeferRehash)
535
0
            set->bRehash = true;
536
0
        else
537
0
        {
538
0
            if (!TIFFHashSetRehash(set))
539
0
            {
540
0
                set->nIndiceAllocatedSize++;
541
0
                return false;
542
0
            }
543
0
        }
544
0
    }
545
546
4
    nHashVal = (int)(set->fnHashFunc(elt) % set->nAllocatedSize);
547
4
    cur = set->tabList[nHashVal];
548
4
    prev = NULL;
549
4
    while (cur)
550
4
    {
551
4
        if (set->fnEqualFunc(cur->pData, elt))
552
4
        {
553
4
            if (prev)
554
0
                prev->psNext = cur->psNext;
555
4
            else
556
4
                set->tabList[nHashVal] = cur->psNext;
557
558
4
            if (set->fnFreeEltFunc)
559
2
                set->fnFreeEltFunc(cur->pData);
560
561
4
            TIFFHashSetReturnListElt(set, cur);
562
#ifdef HASH_DEBUG
563
            if (set->tabList[nHashVal])
564
                set->nCollisions--;
565
#endif
566
4
            set->nSize--;
567
4
            return true;
568
4
        }
569
0
        prev = cur;
570
0
        cur = cur->psNext;
571
0
    }
572
0
    return false;
573
4
}
574
575
/************************************************************************/
576
/*                         TIFFHashSetRemove()                          */
577
/************************************************************************/
578
579
/**
580
 * Removes an element from a hash set
581
 *
582
 * @param set the hash set
583
 * @param elt the new element to remove from the hash set
584
 *
585
 * @return true if the element was in the hash set
586
 */
587
588
bool TIFFHashSetRemove(TIFFHashSet *set, const void *elt)
589
4
{
590
4
    return TIFFHashSetRemoveInternal(set, elt, false);
591
4
}
592
593
#ifdef notused
594
/************************************************************************/
595
/*                     TIFFHashSetRemoveDeferRehash()                   */
596
/************************************************************************/
597
598
/**
599
 * Removes an element from a hash set.
600
 *
601
 * This will defer potential rehashing of the set to later calls to
602
 * TIFFHashSetInsert() or TIFFHashSetRemove().
603
 *
604
 * @param set the hash set
605
 * @param elt the new element to remove from the hash set
606
 *
607
 * @return true if the element was in the hash set
608
 */
609
610
bool TIFFHashSetRemoveDeferRehash(TIFFHashSet *set, const void *elt)
611
{
612
    return TIFFHashSetRemoveInternal(set, elt, true);
613
}
614
#endif