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