aboutsummaryrefslogtreecommitdiff
path: root/src/libs/dutil/WixToolset.DUtil/dictutil.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/dutil/WixToolset.DUtil/dictutil.cpp')
-rw-r--r--src/libs/dutil/WixToolset.DUtil/dictutil.cpp784
1 files changed, 784 insertions, 0 deletions
diff --git a/src/libs/dutil/WixToolset.DUtil/dictutil.cpp b/src/libs/dutil/WixToolset.DUtil/dictutil.cpp
new file mode 100644
index 00000000..0d0743eb
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/dictutil.cpp
@@ -0,0 +1,784 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6// Exit macros
7#define DictExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DICTUTIL, x, s, __VA_ARGS__)
8#define DictExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DICTUTIL, x, s, __VA_ARGS__)
9#define DictExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DICTUTIL, x, s, __VA_ARGS__)
10#define DictExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DICTUTIL, x, s, __VA_ARGS__)
11#define DictExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DICTUTIL, x, s, __VA_ARGS__)
12#define DictExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DICTUTIL, x, s, __VA_ARGS__)
13#define DictExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DICTUTIL, p, x, e, s, __VA_ARGS__)
14#define DictExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DICTUTIL, p, x, s, __VA_ARGS__)
15#define DictExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DICTUTIL, p, x, e, s, __VA_ARGS__)
16#define DictExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DICTUTIL, p, x, s, __VA_ARGS__)
17#define DictExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DICTUTIL, e, x, s, __VA_ARGS__)
18#define DictExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_DICTUTIL, g, x, s, __VA_ARGS__)
19
20// These should all be primes, and spaced reasonably apart (currently each is about 4x the last)
21const DWORD MAX_BUCKET_SIZES[] = {
22 503,
23 2017,
24 7937,
25 32779,
26 131111,
27 524341,
28 2097709,
29 8390857,
30 33563437,
31 134253719,
32 537014927,
33 2148059509
34 };
35
36// However many items are in the cab, let's keep the buckets at least 8 times that to avoid collisions
37#define MAX_BUCKETS_TO_ITEMS_RATIO 8
38
39enum DICT_TYPE
40{
41 DICT_INVALID = 0,
42 DICT_EMBEDDED_KEY = 1,
43 DICT_STRING_LIST = 2
44};
45
46struct STRINGDICT_STRUCT
47{
48 DICT_TYPE dtType;
49
50 // Optional flags to control the behavior of the dictionary.
51 DICT_FLAG dfFlags;
52
53 // Index into MAX_BUCKET_SIZES (array of primes), representing number of buckets we've allocated
54 DWORD dwBucketSizeIndex;
55
56 // Number of items currently stored in the dict buckets
57 DWORD dwNumItems;
58
59 // Byte offset of key within bucket value, for collision checking - see
60 // comments above DictCreateEmbeddedKey() implementation for further details
61 size_t cByteOffset;
62
63 // The actual stored buckets
64 void **ppvBuckets;
65
66 // The actual stored items in the order they were added (used for auto freeing or enumerating)
67 void **ppvItemList;
68
69 // Pointer to the array of items, so the caller is free to resize the array of values out from under us without harm
70 void **ppvValueArray;
71};
72
73const int STRINGDICT_HANDLE_BYTES = sizeof(STRINGDICT_STRUCT);
74
75static HRESULT StringHash(
76 __in const STRINGDICT_STRUCT *psd,
77 __in DWORD dwNumBuckets,
78 __in_z LPCWSTR pszString,
79 __out DWORD *pdwHash
80 );
81static BOOL IsMatchExact(
82 __in const STRINGDICT_STRUCT *psd,
83 __in DWORD dwMatchIndex,
84 __in_z LPCWSTR wzOriginalString
85 );
86static HRESULT GetValue(
87 __in const STRINGDICT_STRUCT *psd,
88 __in_z LPCWSTR pszString,
89 __out_opt void **ppvValue
90 );
91static HRESULT GetInsertIndex(
92 __in const STRINGDICT_STRUCT *psd,
93 __in DWORD dwBucketCount,
94 __in void **ppvBuckets,
95 __in_z LPCWSTR pszString,
96 __out DWORD *pdwOutput
97 );
98static HRESULT GetIndex(
99 __in const STRINGDICT_STRUCT *psd,
100 __in_z LPCWSTR pszString,
101 __out DWORD *pdwOutput
102 );
103static LPCWSTR GetKey(
104 __in const STRINGDICT_STRUCT *psd,
105 __in void *pvValue
106 );
107static HRESULT GrowDictionary(
108 __inout STRINGDICT_STRUCT *psd
109 );
110// These 2 helper functions allow us to safely handle dictutil consumers resizing
111// the value array by storing "offsets" instead of raw void *'s in our buckets.
112static void * TranslateOffsetToValue(
113 __in const STRINGDICT_STRUCT *psd,
114 __in void *pvValue
115 );
116static void * TranslateValueToOffset(
117 __in const STRINGDICT_STRUCT *psd,
118 __in void *pvValue
119 );
120
121// The dict will store a set of keys (as wide-char strings) and a set of values associated with those keys (as void *'s).
122// However, to support collision checking, the key needs to be represented in the "value" object (pointed to
123// by the void *). The "stByteOffset" parameter tells this dict the byte offset of the "key" string pointer
124// within the "value" object. Use the offsetof() macro to fill this out.
125// The "ppvArray" parameter gives dictutil the address of your value array. If you provide this parameter,
126// dictutil will remember all pointer values provided as "offsets" against this array. It is only necessary to provide
127// this parameter to dictutil if it is possible you will realloc the array.
128//
129// Use DictAddValue() and DictGetValue() with this dictionary type.
130extern "C" HRESULT DAPI DictCreateWithEmbeddedKey(
131 __out_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE* psdHandle,
132 __in DWORD dwNumExpectedItems,
133 __in_opt void **ppvArray,
134 __in size_t cByteOffset,
135 __in DICT_FLAG dfFlags
136 )
137{
138 HRESULT hr = S_OK;
139
140 DictExitOnNull(psdHandle, hr, E_INVALIDARG, "Handle not specified while creating dict");
141
142 // Allocate the handle
143 *psdHandle = static_cast<STRINGDICT_HANDLE>(MemAlloc(sizeof(STRINGDICT_STRUCT), FALSE));
144 DictExitOnNull(*psdHandle, hr, E_OUTOFMEMORY, "Failed to allocate dictionary object");
145
146 STRINGDICT_STRUCT *psd = static_cast<STRINGDICT_STRUCT *>(*psdHandle);
147
148 // Fill out the new handle's values
149 psd->dtType = DICT_EMBEDDED_KEY;
150 psd->dfFlags = dfFlags;
151 psd->cByteOffset = cByteOffset;
152 psd->dwBucketSizeIndex = 0;
153 psd->dwNumItems = 0;
154 psd->ppvItemList = NULL;
155 psd->ppvValueArray = ppvArray;
156
157 // Make psd->dwBucketSizeIndex point to the appropriate spot in the prime
158 // array based on expected number of items and items to buckets ratio
159 // Careful: the "-1" in "countof(MAX_BUCKET_SIZES)-1" ensures we don't end
160 // this loop past the end of the array!
161 while (psd->dwBucketSizeIndex < (countof(MAX_BUCKET_SIZES)-1) &&
162 MAX_BUCKET_SIZES[psd->dwBucketSizeIndex] < dwNumExpectedItems * MAX_BUCKETS_TO_ITEMS_RATIO)
163 {
164 ++psd->dwBucketSizeIndex;
165 }
166
167 // Finally, allocate our initial buckets
168 psd->ppvBuckets = static_cast<void**>(MemAlloc(sizeof(void *) * MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], TRUE));
169 DictExitOnNull(psd->ppvBuckets, hr, E_OUTOFMEMORY, "Failed to allocate buckets for dictionary");
170
171LExit:
172 return hr;
173}
174
175// The dict will store a set of keys, with no values associated with them. Use DictAddKey() and DictKeyExists() with this dictionary type.
176extern "C" HRESULT DAPI DictCreateStringList(
177 __out_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE* psdHandle,
178 __in DWORD dwNumExpectedItems,
179 __in DICT_FLAG dfFlags
180 )
181{
182 HRESULT hr = S_OK;
183
184 DictExitOnNull(psdHandle, hr, E_INVALIDARG, "Handle not specified while creating dict");
185
186 // Allocate the handle
187 *psdHandle = static_cast<STRINGDICT_HANDLE>(MemAlloc(sizeof(STRINGDICT_STRUCT), FALSE));
188 DictExitOnNull(*psdHandle, hr, E_OUTOFMEMORY, "Failed to allocate dictionary object");
189
190 STRINGDICT_STRUCT *psd = static_cast<STRINGDICT_STRUCT *>(*psdHandle);
191
192 // Fill out the new handle's values
193 psd->dtType = DICT_STRING_LIST;
194 psd->dfFlags = dfFlags;
195 psd->cByteOffset = 0;
196 psd->dwBucketSizeIndex = 0;
197 psd->dwNumItems = 0;
198 psd->ppvItemList = NULL;
199 psd->ppvValueArray = NULL;
200
201 // Make psd->dwBucketSizeIndex point to the appropriate spot in the prime
202 // array based on expected number of items and items to buckets ratio
203 // Careful: the "-1" in "countof(MAX_BUCKET_SIZES)-1" ensures we don't end
204 // this loop past the end of the array!
205 while (psd->dwBucketSizeIndex < (countof(MAX_BUCKET_SIZES)-1) &&
206 MAX_BUCKET_SIZES[psd->dwBucketSizeIndex] < dwNumExpectedItems * MAX_BUCKETS_TO_ITEMS_RATIO)
207 {
208 ++psd->dwBucketSizeIndex;
209 }
210
211 // Finally, allocate our initial buckets
212 psd->ppvBuckets = static_cast<void**>(MemAlloc(sizeof(void *) * MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], TRUE));
213 DictExitOnNull(psd->ppvBuckets, hr, E_OUTOFMEMORY, "Failed to allocate buckets for dictionary");
214
215LExit:
216 return hr;
217}
218
219extern "C" HRESULT DAPI DictCreateStringListFromArray(
220 __out_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE* psdHandle,
221 __in_ecount(cStringArray) const LPCWSTR* rgwzStringArray,
222 __in const DWORD cStringArray,
223 __in DICT_FLAG dfFlags
224 )
225{
226 HRESULT hr = S_OK;
227 STRINGDICT_HANDLE sd = NULL;
228
229 hr = DictCreateStringList(&sd, cStringArray, dfFlags);
230 DictExitOnFailure(hr, "Failed to create the string dictionary.");
231
232 for (DWORD i = 0; i < cStringArray; ++i)
233 {
234 const LPCWSTR wzKey = rgwzStringArray[i];
235
236 hr = DictKeyExists(sd, wzKey);
237 if (E_NOTFOUND != hr)
238 {
239 DictExitOnFailure(hr, "Failed to check the string dictionary.");
240 }
241 else
242 {
243 hr = DictAddKey(sd, wzKey);
244 DictExitOnFailure(hr, "Failed to add \"%ls\" to the string dictionary.", wzKey);
245 }
246 }
247
248 *psdHandle = sd;
249 sd = NULL;
250
251LExit:
252 ReleaseDict(sd);
253
254 return hr;
255}
256
257extern "C" HRESULT DAPI DictCompareStringListToArray(
258 __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdStringList,
259 __in_ecount(cStringArray) const LPCWSTR* rgwzStringArray,
260 __in const DWORD cStringArray
261 )
262{
263 HRESULT hr = S_OK;
264
265 for (DWORD i = 0; i < cStringArray; ++i)
266 {
267 hr = DictKeyExists(sdStringList, rgwzStringArray[i]);
268 if (E_NOTFOUND != hr)
269 {
270 DictExitOnFailure(hr, "Failed to check the string dictionary.");
271 ExitFunction1(hr = S_OK);
272 }
273 }
274
275 ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_NO_MATCH));
276
277LExit:
278 return hr;
279}
280
281// Todo: Dict should resize itself when (number of items) exceeds (number of buckets / MAX_BUCKETS_TO_ITEMS_RATIO)
282extern "C" HRESULT DAPI DictAddKey(
283 __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle,
284 __in_z LPCWSTR pszString
285 )
286{
287 HRESULT hr = S_OK;
288 DWORD dwIndex = 0;
289 STRINGDICT_STRUCT *psd = static_cast<STRINGDICT_STRUCT *>(sdHandle);
290
291 DictExitOnNull(sdHandle, hr, E_INVALIDARG, "Handle not specified while adding value to dict");
292 DictExitOnNull(pszString, hr, E_INVALIDARG, "String not specified while adding value to dict");
293
294 if (psd->dwBucketSizeIndex >= countof(MAX_BUCKET_SIZES))
295 {
296 hr = E_INVALIDARG;
297 DictExitOnFailure(hr, "Invalid dictionary - bucket size index is out of range");
298 }
299
300 if (DICT_STRING_LIST != psd->dtType)
301 {
302 hr = E_INVALIDARG;
303 DictExitOnFailure(hr, "Tried to add key without value to wrong dictionary type! This dictionary type is: %d", psd->dtType);
304 }
305
306 if ((psd->dwNumItems + 1) >= MAX_BUCKET_SIZES[psd->dwBucketSizeIndex] / MAX_BUCKETS_TO_ITEMS_RATIO)
307 {
308 hr = GrowDictionary(psd);
309 if (HRESULT_FROM_WIN32(ERROR_DATABASE_FULL) == hr)
310 {
311 // If we fail to proactively grow the dictionary, don't fail unless the dictionary is completely full
312 if (psd->dwNumItems < MAX_BUCKET_SIZES[psd->dwBucketSizeIndex])
313 {
314 hr = S_OK;
315 }
316 }
317 DictExitOnFailure(hr, "Failed to grow dictionary");
318 }
319
320 hr = GetInsertIndex(psd, MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], psd->ppvBuckets, pszString, &dwIndex);
321 DictExitOnFailure(hr, "Failed to get index to insert into");
322
323 hr = MemEnsureArraySize(reinterpret_cast<void **>(&(psd->ppvItemList)), psd->dwNumItems + 1, sizeof(void *), 1000);
324 DictExitOnFailure(hr, "Failed to resize list of items in dictionary");
325 ++psd->dwNumItems;
326
327 hr = StrAllocString(reinterpret_cast<LPWSTR *>(&(psd->ppvBuckets[dwIndex])), pszString, 0);
328 DictExitOnFailure(hr, "Failed to allocate copy of string");
329
330 psd->ppvItemList[psd->dwNumItems-1] = psd->ppvBuckets[dwIndex];
331
332LExit:
333 return hr;
334}
335
336// Todo: Dict should resize itself when (number of items) exceeds (number of buckets / MAX_BUCKETS_TO_ITEMS_RATIO)
337extern "C" HRESULT DAPI DictAddValue(
338 __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle,
339 __in void *pvValue
340 )
341{
342 HRESULT hr = S_OK;
343 void *pvOffset = NULL;
344 LPCWSTR wzKey = NULL;
345 DWORD dwIndex = 0;
346 STRINGDICT_STRUCT *psd = static_cast<STRINGDICT_STRUCT *>(sdHandle);
347
348 DictExitOnNull(sdHandle, hr, E_INVALIDARG, "Handle not specified while adding value to dict");
349 DictExitOnNull(pvValue, hr, E_INVALIDARG, "Value not specified while adding value to dict");
350
351 if (psd->dwBucketSizeIndex >= countof(MAX_BUCKET_SIZES))
352 {
353 hr = E_INVALIDARG;
354 DictExitOnFailure(hr, "Invalid dictionary - bucket size index is out of range");
355 }
356
357 if (DICT_EMBEDDED_KEY != psd->dtType)
358 {
359 hr = E_INVALIDARG;
360 DictExitOnFailure(hr, "Tried to add key/value pair to wrong dictionary type! This dictionary type is: %d", psd->dtType);
361 }
362
363 wzKey = GetKey(psd, pvValue);
364 DictExitOnNull(wzKey, hr, E_INVALIDARG, "String not specified while adding value to dict");
365
366 if ((psd->dwNumItems + 1) >= MAX_BUCKET_SIZES[psd->dwBucketSizeIndex] / MAX_BUCKETS_TO_ITEMS_RATIO)
367 {
368 hr = GrowDictionary(psd);
369 if (HRESULT_FROM_WIN32(ERROR_DATABASE_FULL) == hr && psd->dwNumItems + 1 )
370 {
371 // If we fail to proactively grow the dictionary, don't fail unless the dictionary is completely full
372 if (psd->dwNumItems < MAX_BUCKET_SIZES[psd->dwBucketSizeIndex])
373 {
374 hr = S_OK;
375 }
376 }
377 DictExitOnFailure(hr, "Failed to grow dictionary");
378 }
379
380 hr = GetInsertIndex(psd, MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], psd->ppvBuckets, wzKey, &dwIndex);
381 DictExitOnFailure(hr, "Failed to get index to insert into");
382
383 hr = MemEnsureArraySize(reinterpret_cast<void **>(&(psd->ppvItemList)), psd->dwNumItems + 1, sizeof(void *), 1000);
384 DictExitOnFailure(hr, "Failed to resize list of items in dictionary");
385 ++psd->dwNumItems;
386
387 pvOffset = TranslateValueToOffset(psd, pvValue);
388 psd->ppvBuckets[dwIndex] = pvOffset;
389 psd->ppvItemList[psd->dwNumItems-1] = pvOffset;
390
391LExit:
392 return hr;
393}
394
395extern "C" HRESULT DAPI DictGetValue(
396 __in_bcount(STRINGDICT_HANDLE_BYTES) C_STRINGDICT_HANDLE sdHandle,
397 __in_z LPCWSTR pszString,
398 __out void **ppvValue
399 )
400{
401 HRESULT hr = S_OK;
402
403 DictExitOnNull(sdHandle, hr, E_INVALIDARG, "Handle not specified while searching dict");
404 DictExitOnNull(pszString, hr, E_INVALIDARG, "String not specified while searching dict");
405
406 const STRINGDICT_STRUCT *psd = static_cast<const STRINGDICT_STRUCT *>(sdHandle);
407
408 if (DICT_EMBEDDED_KEY != psd->dtType)
409 {
410 hr = E_INVALIDARG;
411 DictExitOnFailure(hr, "Tried to lookup value in wrong dictionary type! This dictionary type is: %d", psd->dtType);
412 }
413
414 hr = GetValue(psd, pszString, ppvValue);
415 if (E_NOTFOUND == hr)
416 {
417 ExitFunction();
418 }
419 DictExitOnFailure(hr, "Failed to call internal GetValue()");
420
421LExit:
422 return hr;
423}
424
425extern "C" HRESULT DAPI DictKeyExists(
426 __in_bcount(STRINGDICT_HANDLE_BYTES) C_STRINGDICT_HANDLE sdHandle,
427 __in_z LPCWSTR pszString
428 )
429{
430 HRESULT hr = S_OK;
431
432 DictExitOnNull(sdHandle, hr, E_INVALIDARG, "Handle not specified while searching dict");
433 DictExitOnNull(pszString, hr, E_INVALIDARG, "String not specified while searching dict");
434
435 const STRINGDICT_STRUCT *psd = static_cast<const STRINGDICT_STRUCT *>(sdHandle);
436
437 // This works with either type of dictionary
438 hr = GetValue(psd, pszString, NULL);
439 if (E_NOTFOUND == hr)
440 {
441 ExitFunction();
442 }
443 DictExitOnFailure(hr, "Failed to call internal GetValue()");
444
445LExit:
446 return hr;
447}
448
449extern "C" void DAPI DictDestroy(
450 __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle
451 )
452{
453 DWORD i;
454
455 STRINGDICT_STRUCT *psd = static_cast<STRINGDICT_STRUCT *>(sdHandle);
456
457 if (DICT_STRING_LIST == psd->dtType)
458 {
459 for (i = 0; i < psd->dwNumItems; ++i)
460 {
461 ReleaseStr(reinterpret_cast<LPWSTR>(psd->ppvItemList[i]));
462 }
463 }
464
465 ReleaseMem(psd->ppvItemList);
466 ReleaseMem(psd->ppvBuckets);
467 ReleaseMem(psd);
468}
469
470static HRESULT StringHash(
471 __in const STRINGDICT_STRUCT *psd,
472 __in DWORD dwNumBuckets,
473 __in_z LPCWSTR pszString,
474 __out DWORD *pdwHash
475 )
476{
477 HRESULT hr = S_OK;
478 LPCWSTR wzKey = NULL;
479 LPWSTR sczNewKey = NULL;
480 DWORD result = 0;
481
482 if (DICT_FLAG_CASEINSENSITIVE & psd->dfFlags)
483 {
484 hr = StrAllocStringToUpperInvariant(&sczNewKey, pszString, 0);
485 DictExitOnFailure(hr, "Failed to convert the string to upper-case.");
486
487 wzKey = sczNewKey;
488 }
489 else
490 {
491 wzKey = pszString;
492 }
493
494 while (*wzKey)
495 {
496 result = ~(*wzKey++ * 509) + result * 65599;
497 }
498
499 *pdwHash = result % dwNumBuckets;
500
501LExit:
502 ReleaseStr(sczNewKey);
503
504 return hr;
505}
506
507static BOOL IsMatchExact(
508 __in const STRINGDICT_STRUCT *psd,
509 __in DWORD dwMatchIndex,
510 __in_z LPCWSTR wzOriginalString
511 )
512{
513 LPCWSTR wzMatchString = GetKey(psd, TranslateOffsetToValue(psd, psd->ppvBuckets[dwMatchIndex]));
514 DWORD dwFlags = 0;
515
516 if (DICT_FLAG_CASEINSENSITIVE & psd->dfFlags)
517 {
518 dwFlags |= NORM_IGNORECASE;
519 }
520
521 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, dwFlags, wzOriginalString, -1, wzMatchString, -1))
522 {
523 return TRUE;
524 }
525
526 return FALSE;
527}
528
529static HRESULT GetValue(
530 __in const STRINGDICT_STRUCT *psd,
531 __in_z LPCWSTR pszString,
532 __out_opt void **ppvValue
533 )
534{
535 HRESULT hr = S_OK;
536 DWORD dwOriginalIndexCandidate = 0;
537 void *pvCandidateValue = NULL;
538 DWORD dwIndex = 0;
539
540 DictExitOnNull(psd, hr, E_INVALIDARG, "Handle not specified while searching dict");
541 DictExitOnNull(pszString, hr, E_INVALIDARG, "String not specified while searching dict");
542
543 if (psd->dwBucketSizeIndex >= countof(MAX_BUCKET_SIZES))
544 {
545 hr = E_INVALIDARG;
546 DictExitOnFailure(hr, "Invalid dictionary - bucket size index is out of range");
547 }
548
549 hr = StringHash(psd, MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], pszString, &dwOriginalIndexCandidate);
550 DictExitOnFailure(hr, "Failed to hash the string.");
551
552 DWORD dwIndexCandidate = dwOriginalIndexCandidate;
553
554 pvCandidateValue = TranslateOffsetToValue(psd, psd->ppvBuckets[dwIndexCandidate]);
555
556 // If no match exists in the dict
557 if (NULL == pvCandidateValue)
558 {
559 if (NULL != ppvValue)
560 {
561 *ppvValue = NULL;
562 }
563 ExitFunction1(hr = E_NOTFOUND);
564 }
565
566 hr = GetIndex(psd, pszString, &dwIndex);
567 if (E_NOTFOUND == hr)
568 {
569 ExitFunction();
570 }
571 DictExitOnFailure(hr, "Failed to find index to get");
572
573 if (NULL != ppvValue)
574 {
575 *ppvValue = TranslateOffsetToValue(psd, psd->ppvBuckets[dwIndex]);
576 }
577
578LExit:
579 if (FAILED(hr) && NULL != ppvValue)
580 {
581 *ppvValue = NULL;
582 }
583
584 return hr;
585}
586
587static HRESULT GetInsertIndex(
588 __in const STRINGDICT_STRUCT *psd,
589 __in DWORD dwBucketCount,
590 __in void **ppvBuckets,
591 __in_z LPCWSTR pszString,
592 __out DWORD *pdwOutput
593 )
594{
595 HRESULT hr = S_OK;
596 DWORD dwOriginalIndexCandidate = 0;
597
598 hr = StringHash(psd, dwBucketCount, pszString, &dwOriginalIndexCandidate);
599 DictExitOnFailure(hr, "Failed to hash the string.");
600
601 DWORD dwIndexCandidate = dwOriginalIndexCandidate;
602
603 // If we collide, keep iterating forward from our intended position, even wrapping around to zero, until we find an empty bucket
604#pragma prefast(push)
605#pragma prefast(disable:26007)
606 while (NULL != ppvBuckets[dwIndexCandidate])
607#pragma prefast(pop)
608 {
609 ++dwIndexCandidate;
610
611 // If we got to the end of the array, wrap around to zero index
612 if (dwIndexCandidate >= dwBucketCount)
613 {
614 dwIndexCandidate = 0;
615 }
616
617 // If we wrapped all the way back around to our original index, the dict is full - throw an error
618 if (dwIndexCandidate == dwOriginalIndexCandidate)
619 {
620 // The dict table is full - this error seems to be a reasonably close match
621 hr = HRESULT_FROM_WIN32(ERROR_DATABASE_FULL);
622 DictExitOnRootFailure(hr, "Failed to add item '%ls' to dict table because dict table is full of items", pszString);
623 }
624 }
625
626 *pdwOutput = dwIndexCandidate;
627
628LExit:
629 return hr;
630}
631
632static HRESULT GetIndex(
633 __in const STRINGDICT_STRUCT *psd,
634 __in_z LPCWSTR pszString,
635 __out DWORD *pdwOutput
636 )
637{
638 HRESULT hr = S_OK;
639 DWORD dwOriginalIndexCandidate = 0;
640
641 if (psd->dwBucketSizeIndex >= countof(MAX_BUCKET_SIZES))
642 {
643 hr = E_INVALIDARG;
644 DictExitOnFailure(hr, "Invalid dictionary - bucket size index is out of range");
645 }
646
647 hr = StringHash(psd, MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], pszString, &dwOriginalIndexCandidate);
648 DictExitOnFailure(hr, "Failed to hash the string.");
649
650 DWORD dwIndexCandidate = dwOriginalIndexCandidate;
651
652 while (!IsMatchExact(psd, dwIndexCandidate, pszString))
653 {
654 ++dwIndexCandidate;
655
656 // If we got to the end of the array, wrap around to zero index
657 if (dwIndexCandidate >= MAX_BUCKET_SIZES[psd->dwBucketSizeIndex])
658 {
659 dwIndexCandidate = 0;
660 }
661
662 // If no match exists in the dict
663 if (NULL == psd->ppvBuckets[dwIndexCandidate])
664 {
665 ExitFunction1(hr = E_NOTFOUND);
666 }
667
668 // If we wrapped all the way back around to our original index, the dict is full and we found nothing, so return as such
669 if (dwIndexCandidate == dwOriginalIndexCandidate)
670 {
671 ExitFunction1(hr = E_NOTFOUND);
672 }
673 }
674
675 *pdwOutput = dwIndexCandidate;
676
677LExit:
678 return hr;
679}
680
681static LPCWSTR GetKey(
682 __in const STRINGDICT_STRUCT *psd,
683 __in void *pvValue
684 )
685{
686 const BYTE *lpByte = reinterpret_cast<BYTE *>(pvValue);
687
688 if (DICT_EMBEDDED_KEY == psd->dtType)
689 {
690 void *pvKey = reinterpret_cast<void *>(reinterpret_cast<BYTE *>(pvValue) + psd->cByteOffset);
691
692#pragma prefast(push)
693#pragma prefast(disable:26010)
694 return *(reinterpret_cast<LPCWSTR *>(pvKey));
695#pragma prefast(pop)
696 }
697 else
698 {
699 return (reinterpret_cast<LPCWSTR>(lpByte));
700 }
701}
702
703static HRESULT GrowDictionary(
704 __inout STRINGDICT_STRUCT *psd
705 )
706{
707 HRESULT hr = S_OK;
708 DWORD dwInsertIndex = 0;
709 LPCWSTR wzKey = NULL;
710 DWORD dwNewBucketSizeIndex = 0;
711 size_t cbAllocSize = 0;
712 void **ppvNewBuckets = NULL;
713
714 dwNewBucketSizeIndex = psd->dwBucketSizeIndex + 1;
715
716 if (dwNewBucketSizeIndex >= countof(MAX_BUCKET_SIZES))
717 {
718 ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_DATABASE_FULL));
719 }
720
721 hr = ::SizeTMult(sizeof(void *), MAX_BUCKET_SIZES[dwNewBucketSizeIndex], &cbAllocSize);
722 DictExitOnFailure(hr, "Overflow while calculating allocation size to grow dictionary");
723
724 ppvNewBuckets = static_cast<void**>(MemAlloc(cbAllocSize, TRUE));
725 DictExitOnNull(ppvNewBuckets, hr, E_OUTOFMEMORY, "Failed to allocate %u buckets while growing dictionary", MAX_BUCKET_SIZES[dwNewBucketSizeIndex]);
726
727 for (DWORD i = 0; i < psd->dwNumItems; ++i)
728 {
729 wzKey = GetKey(psd, TranslateOffsetToValue(psd, psd->ppvItemList[i]));
730 DictExitOnNull(wzKey, hr, E_INVALIDARG, "String not specified in existing dict value");
731
732 hr = GetInsertIndex(psd, MAX_BUCKET_SIZES[dwNewBucketSizeIndex], ppvNewBuckets, wzKey, &dwInsertIndex);
733 DictExitOnFailure(hr, "Failed to get index to insert into");
734
735 ppvNewBuckets[dwInsertIndex] = psd->ppvItemList[i];
736 }
737
738 psd->dwBucketSizeIndex = dwNewBucketSizeIndex;
739 ReleaseMem(psd->ppvBuckets);
740 psd->ppvBuckets = ppvNewBuckets;
741 ppvNewBuckets = NULL;
742
743LExit:
744 ReleaseMem(ppvNewBuckets);
745
746 return hr;
747}
748
749static void * TranslateOffsetToValue(
750 __in const STRINGDICT_STRUCT *psd,
751 __in void *pvValue
752 )
753{
754 if (NULL == pvValue)
755 {
756 return NULL;
757 }
758
759 // All offsets are stored as (real offset + 1), so subtract 1 to get back to the real value
760 if (NULL != psd->ppvValueArray)
761 {
762 return reinterpret_cast<void *>(reinterpret_cast<DWORD_PTR>(pvValue) + reinterpret_cast<DWORD_PTR>(*psd->ppvValueArray) - 1);
763 }
764 else
765 {
766 return pvValue;
767 }
768}
769
770static void * TranslateValueToOffset(
771 __in const STRINGDICT_STRUCT *psd,
772 __in void *pvValue
773 )
774{
775 if (NULL != psd->ppvValueArray)
776 {
777 // 0 has a special meaning - we don't want offset 0 into the array to have NULL for the offset - so add 1 to avoid this issue
778 return reinterpret_cast<void *>(reinterpret_cast<DWORD_PTR>(pvValue) - reinterpret_cast<DWORD_PTR>(*psd->ppvValueArray) + 1);
779 }
780 else
781 {
782 return pvValue;
783 }
784}