Coverage Report

Created: 2025-07-01 06:46

/src/FreeRDP/winpr/libwinpr/utils/collections/HashTable.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * WinPR: Windows Portable Runtime
3
 * System.Collections.Hashtable
4
 *
5
 * Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 */
19
20
#include <winpr/config.h>
21
22
#include <winpr/crt.h>
23
#include <winpr/assert.h>
24
25
#include <winpr/collections.h>
26
27
/**
28
 * This implementation is based on the public domain
29
 * hash table implementation made by Keith Pomakis:
30
 *
31
 * http://www.pomakis.com/hashtable/hashtable.c
32
 * http://www.pomakis.com/hashtable/hashtable.h
33
 */
34
35
typedef struct s_wKeyValuePair wKeyValuePair;
36
37
struct s_wKeyValuePair
38
{
39
  void* key;
40
  void* value;
41
42
  wKeyValuePair* next;
43
  BOOL markedForRemove;
44
};
45
46
struct s_wHashTable
47
{
48
  BOOL synchronized;
49
  CRITICAL_SECTION lock;
50
51
  size_t numOfBuckets;
52
  size_t numOfElements;
53
  float idealRatio;
54
  float lowerRehashThreshold;
55
  float upperRehashThreshold;
56
  wKeyValuePair** bucketArray;
57
58
  HASH_TABLE_HASH_FN hash;
59
  wObject key;
60
  wObject value;
61
62
  DWORD foreachRecursionLevel;
63
  DWORD pendingRemoves;
64
};
65
66
BOOL HashTable_PointerCompare(const void* pointer1, const void* pointer2)
67
0
{
68
0
  return (pointer1 == pointer2);
69
0
}
70
71
UINT32 HashTable_PointerHash(const void* pointer)
72
0
{
73
0
  return ((UINT32)(UINT_PTR)pointer) >> 4;
74
0
}
75
76
BOOL HashTable_StringCompare(const void* string1, const void* string2)
77
0
{
78
0
  if (!string1 || !string2)
79
0
    return (string1 == string2);
80
81
0
  return (strcmp((const char*)string1, (const char*)string2) == 0);
82
0
}
83
84
UINT32 HashTable_StringHash(const void* key)
85
0
{
86
0
  UINT32 c = 0;
87
0
  UINT32 hash = 5381;
88
0
  const BYTE* str = (const BYTE*)key;
89
90
  /* djb2 algorithm */
91
0
  while ((c = *str++) != '\0')
92
0
    hash = (hash * 33) + c;
93
94
0
  return hash;
95
0
}
96
97
void* HashTable_StringClone(const void* str)
98
0
{
99
0
  return winpr_ObjectStringClone(str);
100
0
}
101
102
void HashTable_StringFree(void* str)
103
0
{
104
0
  winpr_ObjectStringFree(str);
105
0
}
106
107
static INLINE BOOL HashTable_IsProbablePrime(size_t oddNumber)
108
0
{
109
0
  for (size_t i = 3; i < 51; i += 2)
110
0
  {
111
0
    if (oddNumber == i)
112
0
      return TRUE;
113
0
    else if (oddNumber % i == 0)
114
0
      return FALSE;
115
0
  }
116
117
0
  return TRUE; /* maybe */
118
0
}
119
120
static INLINE size_t HashTable_CalculateIdealNumOfBuckets(wHashTable* table)
121
0
{
122
0
  WINPR_ASSERT(table);
123
124
0
  const float numOfElements = (float)table->numOfElements;
125
0
  const float tmp = (numOfElements / table->idealRatio);
126
0
  size_t idealNumOfBuckets = (size_t)tmp;
127
128
0
  if (idealNumOfBuckets < 5)
129
0
    idealNumOfBuckets = 5;
130
0
  else
131
0
    idealNumOfBuckets |= 0x01;
132
133
0
  while (!HashTable_IsProbablePrime(idealNumOfBuckets))
134
0
    idealNumOfBuckets += 2;
135
136
0
  return idealNumOfBuckets;
137
0
}
138
139
static INLINE void HashTable_Rehash(wHashTable* table, size_t numOfBuckets)
140
0
{
141
0
  UINT32 hashValue = 0;
142
0
  wKeyValuePair* nextPair = NULL;
143
0
  wKeyValuePair** newBucketArray = NULL;
144
145
0
  WINPR_ASSERT(table);
146
0
  if (numOfBuckets == 0)
147
0
    numOfBuckets = HashTable_CalculateIdealNumOfBuckets(table);
148
149
0
  if (numOfBuckets == table->numOfBuckets)
150
0
    return; /* already the right size! */
151
152
0
  newBucketArray = (wKeyValuePair**)calloc(numOfBuckets, sizeof(wKeyValuePair*));
153
154
0
  if (!newBucketArray)
155
0
  {
156
    /*
157
     * Couldn't allocate memory for the new array.
158
     * This isn't a fatal error; we just can't perform the rehash.
159
     */
160
0
    return;
161
0
  }
162
163
0
  for (size_t index = 0; index < table->numOfBuckets; index++)
164
0
  {
165
0
    wKeyValuePair* pair = table->bucketArray[index];
166
167
0
    while (pair)
168
0
    {
169
0
      nextPair = pair->next;
170
0
      hashValue = table->hash(pair->key) % numOfBuckets;
171
0
      pair->next = newBucketArray[hashValue];
172
0
      newBucketArray[hashValue] = pair;
173
0
      pair = nextPair;
174
0
    }
175
0
  }
176
177
0
  free((void*)table->bucketArray);
178
0
  table->bucketArray = newBucketArray;
179
0
  table->numOfBuckets = numOfBuckets;
180
0
}
181
182
static INLINE BOOL HashTable_Equals(wHashTable* table, const wKeyValuePair* pair, const void* key)
183
0
{
184
0
  WINPR_ASSERT(table);
185
0
  WINPR_ASSERT(pair);
186
0
  WINPR_ASSERT(key);
187
0
  return table->key.fnObjectEquals(key, pair->key);
188
0
}
189
190
static INLINE wKeyValuePair* HashTable_Get(wHashTable* table, const void* key)
191
0
{
192
0
  UINT32 hashValue = 0;
193
0
  wKeyValuePair* pair = NULL;
194
195
0
  WINPR_ASSERT(table);
196
0
  if (!key)
197
0
    return NULL;
198
199
0
  hashValue = table->hash(key) % table->numOfBuckets;
200
0
  pair = table->bucketArray[hashValue];
201
202
0
  while (pair && !HashTable_Equals(table, pair, key))
203
0
    pair = pair->next;
204
205
0
  return pair;
206
0
}
207
208
static INLINE void disposeKey(wHashTable* table, void* key)
209
0
{
210
0
  WINPR_ASSERT(table);
211
0
  if (table->key.fnObjectFree)
212
0
    table->key.fnObjectFree(key);
213
0
}
214
215
static INLINE void disposeValue(wHashTable* table, void* value)
216
0
{
217
0
  WINPR_ASSERT(table);
218
0
  if (table->value.fnObjectFree)
219
0
    table->value.fnObjectFree(value);
220
0
}
221
222
static INLINE void disposePair(wHashTable* table, wKeyValuePair* pair)
223
0
{
224
0
  WINPR_ASSERT(table);
225
0
  if (!pair)
226
0
    return;
227
0
  disposeKey(table, pair->key);
228
0
  disposeValue(table, pair->value);
229
0
  free(pair);
230
0
}
231
232
static INLINE void setKey(wHashTable* table, wKeyValuePair* pair, const void* key)
233
0
{
234
0
  WINPR_ASSERT(table);
235
0
  if (!pair)
236
0
    return;
237
0
  disposeKey(table, pair->key);
238
0
  if (table->key.fnObjectNew)
239
0
    pair->key = table->key.fnObjectNew(key);
240
0
  else
241
0
  {
242
0
    union
243
0
    {
244
0
      const void* cpv;
245
0
      void* pv;
246
0
    } cnv;
247
0
    cnv.cpv = key;
248
0
    pair->key = cnv.pv;
249
0
  }
250
0
}
251
252
static INLINE void setValue(wHashTable* table, wKeyValuePair* pair, const void* value)
253
0
{
254
0
  WINPR_ASSERT(table);
255
0
  if (!pair)
256
0
    return;
257
0
  disposeValue(table, pair->value);
258
0
  if (table->value.fnObjectNew)
259
0
    pair->value = table->value.fnObjectNew(value);
260
0
  else
261
0
  {
262
0
    union
263
0
    {
264
0
      const void* cpv;
265
0
      void* pv;
266
0
    } cnv;
267
0
    cnv.cpv = value;
268
0
    pair->value = cnv.pv;
269
0
  }
270
0
}
271
272
/**
273
 * C equivalent of the C# Hashtable Class:
274
 * http://msdn.microsoft.com/en-us/library/system.collections.hashtable.aspx
275
 */
276
277
/**
278
 * Properties
279
 */
280
281
/**
282
 * Gets the number of key/value pairs contained in the HashTable.
283
 */
284
285
size_t HashTable_Count(wHashTable* table)
286
0
{
287
0
  WINPR_ASSERT(table);
288
0
  return table->numOfElements;
289
0
}
290
291
/**
292
 * Methods
293
 */
294
295
/**
296
 * Adds an element with the specified key and value into the HashTable.
297
 */
298
#if defined(WITH_WINPR_DEPRECATED)
299
int HashTable_Add(wHashTable* table, const void* key, const void* value)
300
{
301
  if (!HashTable_Insert(table, key, value))
302
    return -1;
303
  return 0;
304
}
305
#endif
306
307
BOOL HashTable_Insert(wHashTable* table, const void* key, const void* value)
308
0
{
309
0
  BOOL rc = FALSE;
310
0
  UINT32 hashValue = 0;
311
0
  wKeyValuePair* pair = NULL;
312
0
  wKeyValuePair* newPair = NULL;
313
314
0
  WINPR_ASSERT(table);
315
0
  if (!key || !value)
316
0
    return FALSE;
317
318
0
  if (table->synchronized)
319
0
    EnterCriticalSection(&table->lock);
320
321
0
  hashValue = table->hash(key) % table->numOfBuckets;
322
0
  pair = table->bucketArray[hashValue];
323
324
0
  while (pair && !HashTable_Equals(table, pair, key))
325
0
    pair = pair->next;
326
327
0
  if (pair)
328
0
  {
329
0
    if (pair->markedForRemove)
330
0
    {
331
      /* this entry was set to be removed but will be recycled instead */
332
0
      table->pendingRemoves--;
333
0
      pair->markedForRemove = FALSE;
334
0
      table->numOfElements++;
335
0
    }
336
337
0
    if (pair->key != key)
338
0
    {
339
0
      setKey(table, pair, key);
340
0
    }
341
342
0
    if (pair->value != value)
343
0
    {
344
0
      setValue(table, pair, value);
345
0
    }
346
0
    rc = TRUE;
347
0
  }
348
0
  else
349
0
  {
350
0
    newPair = (wKeyValuePair*)calloc(1, sizeof(wKeyValuePair));
351
352
0
    if (newPair)
353
0
    {
354
0
      setKey(table, newPair, key);
355
0
      setValue(table, newPair, value);
356
0
      newPair->next = table->bucketArray[hashValue];
357
0
      newPair->markedForRemove = FALSE;
358
0
      table->bucketArray[hashValue] = newPair;
359
0
      table->numOfElements++;
360
361
0
      if (!table->foreachRecursionLevel && table->upperRehashThreshold > table->idealRatio)
362
0
      {
363
0
        float elementToBucketRatio =
364
0
            (float)table->numOfElements / (float)table->numOfBuckets;
365
366
0
        if (elementToBucketRatio > table->upperRehashThreshold)
367
0
          HashTable_Rehash(table, 0);
368
0
      }
369
0
      rc = TRUE;
370
0
    }
371
0
  }
372
373
0
  if (table->synchronized)
374
0
    LeaveCriticalSection(&table->lock);
375
376
0
  return rc;
377
0
}
378
379
/**
380
 * Removes the element with the specified key from the HashTable.
381
 */
382
383
BOOL HashTable_Remove(wHashTable* table, const void* key)
384
0
{
385
0
  UINT32 hashValue = 0;
386
0
  BOOL status = TRUE;
387
0
  wKeyValuePair* pair = NULL;
388
0
  wKeyValuePair* previousPair = NULL;
389
390
0
  WINPR_ASSERT(table);
391
0
  if (!key)
392
0
    return FALSE;
393
394
0
  if (table->synchronized)
395
0
    EnterCriticalSection(&table->lock);
396
397
0
  hashValue = table->hash(key) % table->numOfBuckets;
398
0
  pair = table->bucketArray[hashValue];
399
400
0
  while (pair && !HashTable_Equals(table, pair, key))
401
0
  {
402
0
    previousPair = pair;
403
0
    pair = pair->next;
404
0
  }
405
406
0
  if (!pair)
407
0
  {
408
0
    status = FALSE;
409
0
    goto out;
410
0
  }
411
412
0
  if (table->foreachRecursionLevel)
413
0
  {
414
    /* if we are running a HashTable_Foreach, just mark the entry for removal */
415
0
    pair->markedForRemove = TRUE;
416
0
    table->pendingRemoves++;
417
0
    table->numOfElements--;
418
0
    goto out;
419
0
  }
420
421
0
  if (previousPair)
422
0
    previousPair->next = pair->next;
423
0
  else
424
0
    table->bucketArray[hashValue] = pair->next;
425
426
0
  disposePair(table, pair);
427
0
  table->numOfElements--;
428
429
0
  if (!table->foreachRecursionLevel && table->lowerRehashThreshold > 0.0f)
430
0
  {
431
0
    float elementToBucketRatio = (float)table->numOfElements / (float)table->numOfBuckets;
432
433
0
    if (elementToBucketRatio < table->lowerRehashThreshold)
434
0
      HashTable_Rehash(table, 0);
435
0
  }
436
437
0
out:
438
0
  if (table->synchronized)
439
0
    LeaveCriticalSection(&table->lock);
440
441
0
  return status;
442
0
}
443
444
/**
445
 * Get an item value using key
446
 */
447
448
void* HashTable_GetItemValue(wHashTable* table, const void* key)
449
0
{
450
0
  void* value = NULL;
451
0
  wKeyValuePair* pair = NULL;
452
453
0
  WINPR_ASSERT(table);
454
0
  if (!key)
455
0
    return NULL;
456
457
0
  if (table->synchronized)
458
0
    EnterCriticalSection(&table->lock);
459
460
0
  pair = HashTable_Get(table, key);
461
462
0
  if (pair && !pair->markedForRemove)
463
0
    value = pair->value;
464
465
0
  if (table->synchronized)
466
0
    LeaveCriticalSection(&table->lock);
467
468
0
  return value;
469
0
}
470
471
/**
472
 * Set an item value using key
473
 */
474
475
BOOL HashTable_SetItemValue(wHashTable* table, const void* key, const void* value)
476
0
{
477
0
  BOOL status = TRUE;
478
0
  wKeyValuePair* pair = NULL;
479
480
0
  WINPR_ASSERT(table);
481
0
  if (!key)
482
0
    return FALSE;
483
484
0
  if (table->synchronized)
485
0
    EnterCriticalSection(&table->lock);
486
487
0
  pair = HashTable_Get(table, key);
488
489
0
  if (!pair || pair->markedForRemove)
490
0
    status = FALSE;
491
0
  else
492
0
  {
493
0
    setValue(table, pair, value);
494
0
  }
495
496
0
  if (table->synchronized)
497
0
    LeaveCriticalSection(&table->lock);
498
499
0
  return status;
500
0
}
501
502
/**
503
 * Removes all elements from the HashTable.
504
 */
505
506
void HashTable_Clear(wHashTable* table)
507
0
{
508
0
  wKeyValuePair* nextPair = NULL;
509
510
0
  WINPR_ASSERT(table);
511
512
0
  if (table->synchronized)
513
0
    EnterCriticalSection(&table->lock);
514
515
0
  for (size_t index = 0; index < table->numOfBuckets; index++)
516
0
  {
517
0
    wKeyValuePair* pair = table->bucketArray[index];
518
519
0
    while (pair)
520
0
    {
521
0
      nextPair = pair->next;
522
523
0
      if (table->foreachRecursionLevel)
524
0
      {
525
        /* if we're in a foreach we just mark the entry for removal */
526
0
        pair->markedForRemove = TRUE;
527
0
        table->pendingRemoves++;
528
0
      }
529
0
      else
530
0
      {
531
0
        disposePair(table, pair);
532
0
        pair = nextPair;
533
0
      }
534
0
    }
535
536
0
    table->bucketArray[index] = NULL;
537
0
  }
538
539
0
  table->numOfElements = 0;
540
0
  if (table->foreachRecursionLevel == 0)
541
0
    HashTable_Rehash(table, 5);
542
543
0
  if (table->synchronized)
544
0
    LeaveCriticalSection(&table->lock);
545
0
}
546
547
/**
548
 * Gets the list of keys as an array
549
 */
550
551
size_t HashTable_GetKeys(wHashTable* table, ULONG_PTR** ppKeys)
552
0
{
553
0
  size_t iKey = 0;
554
0
  size_t count = 0;
555
0
  ULONG_PTR* pKeys = NULL;
556
0
  wKeyValuePair* nextPair = NULL;
557
558
0
  WINPR_ASSERT(table);
559
560
0
  if (table->synchronized)
561
0
    EnterCriticalSection(&table->lock);
562
563
0
  iKey = 0;
564
0
  count = table->numOfElements;
565
0
  if (ppKeys)
566
0
    *ppKeys = NULL;
567
568
0
  if (count < 1)
569
0
  {
570
0
    if (table->synchronized)
571
0
      LeaveCriticalSection(&table->lock);
572
573
0
    return 0;
574
0
  }
575
576
0
  pKeys = (ULONG_PTR*)calloc(count, sizeof(ULONG_PTR));
577
578
0
  if (!pKeys)
579
0
  {
580
0
    if (table->synchronized)
581
0
      LeaveCriticalSection(&table->lock);
582
583
0
    return 0;
584
0
  }
585
586
0
  for (size_t index = 0; index < table->numOfBuckets; index++)
587
0
  {
588
0
    wKeyValuePair* pair = table->bucketArray[index];
589
590
0
    while (pair)
591
0
    {
592
0
      nextPair = pair->next;
593
0
      if (!pair->markedForRemove)
594
0
        pKeys[iKey++] = (ULONG_PTR)pair->key;
595
0
      pair = nextPair;
596
0
    }
597
0
  }
598
599
0
  if (table->synchronized)
600
0
    LeaveCriticalSection(&table->lock);
601
602
0
  if (ppKeys)
603
0
    *ppKeys = pKeys;
604
0
  else
605
0
    free(pKeys);
606
0
  return count;
607
0
}
608
609
BOOL HashTable_Foreach(wHashTable* table, HASH_TABLE_FOREACH_FN fn, VOID* arg)
610
0
{
611
0
  BOOL ret = TRUE;
612
613
0
  WINPR_ASSERT(table);
614
0
  WINPR_ASSERT(fn);
615
616
0
  if (table->synchronized)
617
0
    EnterCriticalSection(&table->lock);
618
619
0
  table->foreachRecursionLevel++;
620
0
  for (size_t index = 0; index < table->numOfBuckets; index++)
621
0
  {
622
0
    for (wKeyValuePair* pair = table->bucketArray[index]; pair; pair = pair->next)
623
0
    {
624
0
      if (!pair->markedForRemove && !fn(pair->key, pair->value, arg))
625
0
      {
626
0
        ret = FALSE;
627
0
        goto out;
628
0
      }
629
0
    }
630
0
  }
631
0
  table->foreachRecursionLevel--;
632
633
0
  if (!table->foreachRecursionLevel && table->pendingRemoves)
634
0
  {
635
    /* if we're the last recursive foreach call, let's do the cleanup if needed */
636
0
    wKeyValuePair** prevPtr = NULL;
637
0
    for (size_t index = 0; index < table->numOfBuckets; index++)
638
0
    {
639
0
      wKeyValuePair* nextPair = NULL;
640
0
      prevPtr = &table->bucketArray[index];
641
0
      for (wKeyValuePair* pair = table->bucketArray[index]; pair;)
642
0
      {
643
0
        nextPair = pair->next;
644
645
0
        if (pair->markedForRemove)
646
0
        {
647
0
          disposePair(table, pair);
648
0
          *prevPtr = nextPair;
649
0
        }
650
0
        else
651
0
        {
652
0
          prevPtr = &pair->next;
653
0
        }
654
0
        pair = nextPair;
655
0
      }
656
0
    }
657
0
    table->pendingRemoves = 0;
658
0
  }
659
660
0
out:
661
0
  if (table->synchronized)
662
0
    LeaveCriticalSection(&table->lock);
663
0
  return ret;
664
0
}
665
666
/**
667
 * Determines whether the HashTable contains a specific key.
668
 */
669
670
BOOL HashTable_Contains(wHashTable* table, const void* key)
671
0
{
672
0
  BOOL status = 0;
673
0
  wKeyValuePair* pair = NULL;
674
675
0
  WINPR_ASSERT(table);
676
0
  if (!key)
677
0
    return FALSE;
678
679
0
  if (table->synchronized)
680
0
    EnterCriticalSection(&table->lock);
681
682
0
  pair = HashTable_Get(table, key);
683
0
  status = (pair && !pair->markedForRemove);
684
685
0
  if (table->synchronized)
686
0
    LeaveCriticalSection(&table->lock);
687
688
0
  return status;
689
0
}
690
691
/**
692
 * Determines whether the HashTable contains a specific key.
693
 */
694
695
BOOL HashTable_ContainsKey(wHashTable* table, const void* key)
696
0
{
697
0
  BOOL status = 0;
698
0
  wKeyValuePair* pair = NULL;
699
700
0
  WINPR_ASSERT(table);
701
0
  if (!key)
702
0
    return FALSE;
703
704
0
  if (table->synchronized)
705
0
    EnterCriticalSection(&table->lock);
706
707
0
  pair = HashTable_Get(table, key);
708
0
  status = (pair && !pair->markedForRemove);
709
710
0
  if (table->synchronized)
711
0
    LeaveCriticalSection(&table->lock);
712
713
0
  return status;
714
0
}
715
716
/**
717
 * Determines whether the HashTable contains a specific value.
718
 */
719
720
BOOL HashTable_ContainsValue(wHashTable* table, const void* value)
721
0
{
722
0
  BOOL status = FALSE;
723
724
0
  WINPR_ASSERT(table);
725
0
  if (!value)
726
0
    return FALSE;
727
728
0
  if (table->synchronized)
729
0
    EnterCriticalSection(&table->lock);
730
731
0
  for (size_t index = 0; index < table->numOfBuckets; index++)
732
0
  {
733
0
    wKeyValuePair* pair = table->bucketArray[index];
734
735
0
    while (pair)
736
0
    {
737
0
      if (!pair->markedForRemove && HashTable_Equals(table, pair, value))
738
0
      {
739
0
        status = TRUE;
740
0
        break;
741
0
      }
742
743
0
      pair = pair->next;
744
0
    }
745
746
0
    if (status)
747
0
      break;
748
0
  }
749
750
0
  if (table->synchronized)
751
0
    LeaveCriticalSection(&table->lock);
752
753
0
  return status;
754
0
}
755
756
/**
757
 * Construction, Destruction
758
 */
759
760
wHashTable* HashTable_New(BOOL synchronized)
761
0
{
762
0
  wHashTable* table = (wHashTable*)calloc(1, sizeof(wHashTable));
763
764
0
  if (!table)
765
0
    goto fail;
766
767
0
  table->synchronized = synchronized;
768
0
  InitializeCriticalSectionAndSpinCount(&(table->lock), 4000);
769
0
  table->numOfBuckets = 64;
770
0
  table->numOfElements = 0;
771
0
  table->bucketArray = (wKeyValuePair**)calloc(table->numOfBuckets, sizeof(wKeyValuePair*));
772
773
0
  if (!table->bucketArray)
774
0
    goto fail;
775
776
0
  table->idealRatio = 3.0f;
777
0
  table->lowerRehashThreshold = 0.0f;
778
0
  table->upperRehashThreshold = 15.0f;
779
0
  table->hash = HashTable_PointerHash;
780
0
  table->key.fnObjectEquals = HashTable_PointerCompare;
781
0
  table->value.fnObjectEquals = HashTable_PointerCompare;
782
783
0
  return table;
784
0
fail:
785
0
  WINPR_PRAGMA_DIAG_PUSH
786
0
  WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
787
0
  HashTable_Free(table);
788
0
  WINPR_PRAGMA_DIAG_POP
789
0
  return NULL;
790
0
}
791
792
void HashTable_Free(wHashTable* table)
793
0
{
794
0
  wKeyValuePair* pair = NULL;
795
0
  wKeyValuePair* nextPair = NULL;
796
797
0
  if (!table)
798
0
    return;
799
800
0
  if (table->bucketArray)
801
0
  {
802
0
    for (size_t index = 0; index < table->numOfBuckets; index++)
803
0
    {
804
0
      pair = table->bucketArray[index];
805
806
0
      while (pair)
807
0
      {
808
0
        nextPair = pair->next;
809
810
0
        disposePair(table, pair);
811
0
        pair = nextPair;
812
0
      }
813
0
    }
814
0
    free((void*)table->bucketArray);
815
0
  }
816
0
  DeleteCriticalSection(&(table->lock));
817
818
0
  free(table);
819
0
}
820
821
void HashTable_Lock(wHashTable* table)
822
0
{
823
0
  WINPR_ASSERT(table);
824
0
  EnterCriticalSection(&table->lock);
825
0
}
826
827
void HashTable_Unlock(wHashTable* table)
828
0
{
829
0
  WINPR_ASSERT(table);
830
0
  LeaveCriticalSection(&table->lock);
831
0
}
832
833
wObject* HashTable_KeyObject(wHashTable* table)
834
0
{
835
0
  WINPR_ASSERT(table);
836
0
  return &table->key;
837
0
}
838
839
wObject* HashTable_ValueObject(wHashTable* table)
840
0
{
841
0
  WINPR_ASSERT(table);
842
0
  return &table->value;
843
0
}
844
845
BOOL HashTable_SetHashFunction(wHashTable* table, HASH_TABLE_HASH_FN fn)
846
0
{
847
0
  WINPR_ASSERT(table);
848
0
  table->hash = fn;
849
0
  return fn != NULL;
850
0
}
851
852
BOOL HashTable_SetupForStringData(wHashTable* table, BOOL stringValues)
853
0
{
854
0
  wObject* obj = NULL;
855
856
0
  if (!HashTable_SetHashFunction(table, HashTable_StringHash))
857
0
    return FALSE;
858
859
0
  obj = HashTable_KeyObject(table);
860
0
  obj->fnObjectEquals = HashTable_StringCompare;
861
0
  obj->fnObjectNew = HashTable_StringClone;
862
0
  obj->fnObjectFree = HashTable_StringFree;
863
864
0
  if (stringValues)
865
0
  {
866
0
    obj = HashTable_ValueObject(table);
867
0
    obj->fnObjectEquals = HashTable_StringCompare;
868
0
    obj->fnObjectNew = HashTable_StringClone;
869
0
    obj->fnObjectFree = HashTable_StringFree;
870
0
  }
871
0
  return TRUE;
872
0
}