Coverage Report

Created: 2024-05-20 06:11

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