Coverage Report

Created: 2026-04-12 06:59

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