Coverage Report

Created: 2024-06-18 06:05

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