Coverage Report

Created: 2023-09-25 06:56

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