xref: /reactos/dll/win32/scrrun/dictionary.c (revision 019f21ee)
1 /*
2  * Copyright (C) 2012 Alistair Leslie-Hughes
3  * Copyright 2015 Nikolay Sivov for CodeWeavers
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18  */
19 #define COBJMACROS
20 
21 #ifdef __REACTOS__
22 #include <wine/config.h>
23 #include <wine/port.h>
24 #endif
25 #include <stdarg.h>
26 #include <math.h>
27 
28 #include "windef.h"
29 #include "winbase.h"
30 #include "ole2.h"
31 #include "olectl.h"
32 #include "dispex.h"
33 #include "scrrun.h"
34 #include "scrrun_private.h"
35 
36 #include "wine/debug.h"
37 #include "wine/heap.h"
38 #include "wine/list.h"
39 
40 WINE_DEFAULT_DEBUG_CHANNEL(scrrun);
41 
42 #define BUCKET_COUNT  509
43 #define DICT_HASH_MOD 1201
44 
45 /* Implementation details
46 
47    Dictionary contains one list that links all pairs, this way
48    order in which they were added is preserved. Each bucket has
49    its own list to hold all pairs in this bucket. Initially all
50    bucket lists are zeroed and we init them once we about to add
51    first pair.
52 
53    When pair is removed it's unlinked from both lists; if it was
54    a last pair in a bucket list it stays empty in initialized state.
55 
56    Preserving pair order is important for enumeration, so far testing
57    indicates that pairs are not reordered basing on hash value.
58  */
59 
60 struct keyitem_pair {
61     struct  list entry;
62     struct  list bucket;
63     DWORD   hash;
64     VARIANT key;
65     VARIANT item;
66 };
67 
68 typedef struct
69 {
70     struct provideclassinfo classinfo;
71     IDictionary IDictionary_iface;
72     LONG ref;
73 
74     CompareMethod method;
75     LONG count;
76     struct list pairs;
77     struct list buckets[BUCKET_COUNT];
78     struct list notifier;
79 } dictionary;
80 
81 struct dictionary_enum {
82     IEnumVARIANT IEnumVARIANT_iface;
83     LONG ref;
84 
85     dictionary *dict;
86     struct list *cur;
87     struct list notify;
88 };
89 
90 static inline dictionary *impl_from_IDictionary(IDictionary *iface)
91 {
92     return CONTAINING_RECORD(iface, dictionary, IDictionary_iface);
93 }
94 
95 static inline struct dictionary_enum *impl_from_IEnumVARIANT(IEnumVARIANT *iface)
96 {
97     return CONTAINING_RECORD(iface, struct dictionary_enum, IEnumVARIANT_iface);
98 }
99 
100 static inline struct list *get_bucket_head(dictionary *dict, DWORD hash)
101 {
102     return &dict->buckets[hash % BUCKET_COUNT];
103 }
104 
105 static inline BOOL is_string_key(const VARIANT *key)
106 {
107     return V_VT(key) == VT_BSTR || V_VT(key) == (VT_BSTR|VT_BYREF);
108 }
109 
110 /* Only for VT_BSTR or VT_BSTR|VT_BYREF types */
111 static inline WCHAR *get_key_strptr(const VARIANT *key)
112 {
113     if (V_VT(key) == VT_BSTR)
114         return V_BSTR(key);
115 
116     if (V_BSTRREF(key))
117         return *V_BSTRREF(key);
118 
119     return NULL;
120 }
121 
122 /* should be used only when both keys are of string type, it's not checked */
123 static inline int strcmp_key(const dictionary *dict, const VARIANT *key1, const VARIANT *key2)
124 {
125     const WCHAR *str1, *str2;
126 
127     str1 = get_key_strptr(key1);
128     str2 = get_key_strptr(key2);
129     return dict->method == BinaryCompare ? wcscmp(str1, str2) : wcsicmp(str1, str2);
130 }
131 
132 static BOOL is_matching_key(const dictionary *dict, const struct keyitem_pair *pair, const VARIANT *key, DWORD hash)
133 {
134     if (is_string_key(key) && is_string_key(&pair->key)) {
135         if (hash != pair->hash)
136             return FALSE;
137 
138         return strcmp_key(dict, key, &pair->key) == 0;
139     }
140 
141     if ((is_string_key(key) && !is_string_key(&pair->key)) ||
142         (!is_string_key(key) && is_string_key(&pair->key)))
143         return FALSE;
144 
145     /* for numeric keys only check hash */
146     return hash == pair->hash;
147 }
148 
149 static struct keyitem_pair *get_keyitem_pair(dictionary *dict, VARIANT *key)
150 {
151     struct keyitem_pair *pair;
152     struct list *head, *entry;
153     VARIANT hash;
154     HRESULT hr;
155 
156     hr = IDictionary_get_HashVal(&dict->IDictionary_iface, key, &hash);
157     if (FAILED(hr))
158         return NULL;
159 
160     head = get_bucket_head(dict, V_I4(&hash));
161     if (!head->next || list_empty(head))
162         return NULL;
163 
164     entry = list_head(head);
165     do {
166         pair = LIST_ENTRY(entry, struct keyitem_pair, bucket);
167         if (is_matching_key(dict, pair, key, V_I4(&hash))) return pair;
168     } while ((entry = list_next(head, entry)));
169 
170     return NULL;
171 }
172 
173 static HRESULT add_keyitem_pair(dictionary *dict, VARIANT *key, VARIANT *item)
174 {
175     struct keyitem_pair *pair;
176     struct list *head;
177     VARIANT hash;
178     HRESULT hr;
179 
180     hr = IDictionary_get_HashVal(&dict->IDictionary_iface, key, &hash);
181     if (FAILED(hr))
182         return hr;
183 
184     pair = heap_alloc(sizeof(*pair));
185     if (!pair)
186         return E_OUTOFMEMORY;
187 
188     pair->hash = V_I4(&hash);
189     VariantInit(&pair->key);
190     VariantInit(&pair->item);
191 
192     hr = VariantCopyInd(&pair->key, key);
193     if (FAILED(hr))
194         goto failed;
195 
196     hr = VariantCopyInd(&pair->item, item);
197     if (FAILED(hr))
198         goto failed;
199 
200     head = get_bucket_head(dict, pair->hash);
201     if (!head->next)
202         /* this only happens once per bucket */
203         list_init(head);
204 
205     /* link to bucket list and to full list */
206     list_add_tail(head, &pair->bucket);
207     list_add_tail(&dict->pairs, &pair->entry);
208     dict->count++;
209     return S_OK;
210 
211 failed:
212     VariantClear(&pair->key);
213     VariantClear(&pair->item);
214     heap_free(pair);
215     return hr;
216 }
217 
218 static void free_keyitem_pair(struct keyitem_pair *pair)
219 {
220     VariantClear(&pair->key);
221     VariantClear(&pair->item);
222     heap_free(pair);
223 }
224 
225 static HRESULT WINAPI dict_enum_QueryInterface(IEnumVARIANT *iface, REFIID riid, void **obj)
226 {
227     struct dictionary_enum *This = impl_from_IEnumVARIANT(iface);
228 
229     TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), obj);
230 
231     if (IsEqualIID(riid, &IID_IEnumVARIANT) || IsEqualIID(riid, &IID_IUnknown)) {
232         *obj = iface;
233         IEnumVARIANT_AddRef(iface);
234         return S_OK;
235     }
236     else {
237         WARN("interface not supported %s\n", debugstr_guid(riid));
238         *obj = NULL;
239         return E_NOINTERFACE;
240     }
241 }
242 
243 static ULONG WINAPI dict_enum_AddRef(IEnumVARIANT *iface)
244 {
245     struct dictionary_enum *This = impl_from_IEnumVARIANT(iface);
246     ULONG ref = InterlockedIncrement(&This->ref);
247     TRACE("(%p)->(%u)\n", This, ref);
248     return ref;
249 }
250 
251 static ULONG WINAPI dict_enum_Release(IEnumVARIANT *iface)
252 {
253     struct dictionary_enum *This = impl_from_IEnumVARIANT(iface);
254     LONG ref = InterlockedDecrement(&This->ref);
255 
256     TRACE("(%p)->(%u)\n", This, ref);
257 
258     if (!ref) {
259         list_remove(&This->notify);
260         IDictionary_Release(&This->dict->IDictionary_iface);
261         heap_free(This);
262     }
263 
264     return ref;
265 }
266 
267 static HRESULT WINAPI dict_enum_Next(IEnumVARIANT *iface, ULONG count, VARIANT *keys, ULONG *fetched)
268 {
269     struct dictionary_enum *This = impl_from_IEnumVARIANT(iface);
270     struct keyitem_pair *pair;
271     ULONG i = 0;
272 
273     TRACE("(%p)->(%u %p %p)\n", This, count, keys, fetched);
274 
275     if (fetched)
276         *fetched = 0;
277 
278     if (!count)
279         return S_OK;
280 
281     while (This->cur && i < count) {
282         pair = LIST_ENTRY(This->cur, struct keyitem_pair, entry);
283         VariantCopy(&keys[i], &pair->key);
284         This->cur = list_next(&This->dict->pairs, This->cur);
285         i++;
286     }
287 
288     if (fetched)
289         *fetched = i;
290 
291     return i < count ? S_FALSE : S_OK;
292 }
293 
294 static HRESULT WINAPI dict_enum_Skip(IEnumVARIANT *iface, ULONG count)
295 {
296     struct dictionary_enum *This = impl_from_IEnumVARIANT(iface);
297 
298     TRACE("(%p)->(%u)\n", This, count);
299 
300     if (!count)
301         return S_OK;
302 
303     if (!This->cur)
304         return S_FALSE;
305 
306     while (count--) {
307         This->cur = list_next(&This->dict->pairs, This->cur);
308         if (!This->cur) break;
309     }
310 
311     return count == 0 ? S_OK : S_FALSE;
312 }
313 
314 static HRESULT WINAPI dict_enum_Reset(IEnumVARIANT *iface)
315 {
316     struct dictionary_enum *This = impl_from_IEnumVARIANT(iface);
317 
318     TRACE("(%p)\n", This);
319 
320     This->cur = list_head(&This->dict->pairs);
321     return S_OK;
322 }
323 
324 static HRESULT create_dict_enum(dictionary*, IUnknown**);
325 
326 static HRESULT WINAPI dict_enum_Clone(IEnumVARIANT *iface, IEnumVARIANT **cloned)
327 {
328     struct dictionary_enum *This = impl_from_IEnumVARIANT(iface);
329     TRACE("(%p)->(%p)\n", This, cloned);
330     return create_dict_enum(This->dict, (IUnknown**)cloned);
331 }
332 
333 static const IEnumVARIANTVtbl dictenumvtbl = {
334     dict_enum_QueryInterface,
335     dict_enum_AddRef,
336     dict_enum_Release,
337     dict_enum_Next,
338     dict_enum_Skip,
339     dict_enum_Reset,
340     dict_enum_Clone
341 };
342 
343 static HRESULT create_dict_enum(dictionary *dict, IUnknown **ret)
344 {
345     struct dictionary_enum *This;
346 
347     *ret = NULL;
348 
349     This = heap_alloc(sizeof(*This));
350     if (!This)
351         return E_OUTOFMEMORY;
352 
353     This->IEnumVARIANT_iface.lpVtbl = &dictenumvtbl;
354     This->ref = 1;
355     This->cur = list_head(&dict->pairs);
356     list_add_tail(&dict->notifier, &This->notify);
357     This->dict = dict;
358     IDictionary_AddRef(&dict->IDictionary_iface);
359 
360     *ret = (IUnknown*)&This->IEnumVARIANT_iface;
361     return S_OK;
362 }
363 
364 static void notify_remove_pair(struct list *notifier, struct list *pair)
365 {
366     struct dictionary_enum *dict_enum;
367     struct list *cur;
368 
369     LIST_FOR_EACH(cur, notifier) {
370         dict_enum = LIST_ENTRY(cur, struct dictionary_enum, notify);
371         if (!pair)
372             dict_enum->cur = list_head(&dict_enum->dict->pairs);
373         else if (dict_enum->cur == pair) {
374             dict_enum->cur = list_next(&dict_enum->dict->pairs, dict_enum->cur);
375         }
376     }
377 }
378 
379 static HRESULT WINAPI dictionary_QueryInterface(IDictionary *iface, REFIID riid, void **obj)
380 {
381     dictionary *This = impl_from_IDictionary(iface);
382     TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), obj);
383 
384     *obj = NULL;
385 
386     if(IsEqualIID(riid, &IID_IUnknown) ||
387        IsEqualIID(riid, &IID_IDispatch) ||
388        IsEqualIID(riid, &IID_IDictionary))
389     {
390         *obj = &This->IDictionary_iface;
391     }
392     else if (IsEqualIID(riid, &IID_IProvideClassInfo))
393     {
394         *obj = &This->classinfo.IProvideClassInfo_iface;
395     }
396     else if ( IsEqualGUID( riid, &IID_IDispatchEx ))
397     {
398         TRACE("Interface IDispatchEx not supported - returning NULL\n");
399         *obj = NULL;
400         return E_NOINTERFACE;
401     }
402     else if ( IsEqualGUID( riid, &IID_IObjectWithSite ))
403     {
404         TRACE("Interface IObjectWithSite not supported - returning NULL\n");
405         *obj = NULL;
406         return E_NOINTERFACE;
407     }
408     else
409     {
410         WARN("interface %s not implemented\n", debugstr_guid(riid));
411         return E_NOINTERFACE;
412     }
413 
414     IUnknown_AddRef((IUnknown*)*obj);
415     return S_OK;
416 }
417 
418 static ULONG WINAPI dictionary_AddRef(IDictionary *iface)
419 {
420     dictionary *This = impl_from_IDictionary(iface);
421     ULONG ref = InterlockedIncrement(&This->ref);
422 
423     TRACE("(%p)->(%u)\n", This, ref);
424 
425     return ref;
426 }
427 
428 static ULONG WINAPI dictionary_Release(IDictionary *iface)
429 {
430     dictionary *This = impl_from_IDictionary(iface);
431     ULONG ref = InterlockedDecrement(&This->ref);
432 
433     TRACE("(%p)->(%u)\n", This, ref);
434 
435     if (!ref) {
436         IDictionary_RemoveAll(iface);
437         heap_free(This);
438     }
439 
440     return ref;
441 }
442 
443 static HRESULT WINAPI dictionary_GetTypeInfoCount(IDictionary *iface, UINT *pctinfo)
444 {
445     dictionary *This = impl_from_IDictionary(iface);
446 
447     TRACE("(%p)->(%p)\n", This, pctinfo);
448 
449     *pctinfo = 1;
450     return S_OK;
451 }
452 
453 static HRESULT WINAPI dictionary_GetTypeInfo(IDictionary *iface, UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
454 {
455     dictionary *This = impl_from_IDictionary(iface);
456 
457     TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
458     return get_typeinfo(IDictionary_tid, ppTInfo);
459 }
460 
461 static HRESULT WINAPI dictionary_GetIDsOfNames(IDictionary *iface, REFIID riid, LPOLESTR *rgszNames,
462                 UINT cNames, LCID lcid, DISPID *rgDispId)
463 {
464     dictionary *This = impl_from_IDictionary(iface);
465     ITypeInfo *typeinfo;
466     HRESULT hr;
467 
468     TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames, lcid, rgDispId);
469 
470     hr = get_typeinfo(IDictionary_tid, &typeinfo);
471     if(SUCCEEDED(hr))
472     {
473         hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
474         ITypeInfo_Release(typeinfo);
475     }
476 
477     return hr;
478 }
479 
480 static HRESULT WINAPI dictionary_Invoke(IDictionary *iface, DISPID dispIdMember, REFIID riid,
481                 LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult,
482                 EXCEPINFO *pExcepInfo, UINT *puArgErr)
483 {
484     dictionary *This = impl_from_IDictionary(iface);
485     ITypeInfo *typeinfo;
486     HRESULT hr;
487 
488     TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
489            lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
490 
491     hr = get_typeinfo(IDictionary_tid, &typeinfo);
492     if(SUCCEEDED(hr))
493     {
494         hr = ITypeInfo_Invoke(typeinfo, &This->IDictionary_iface, dispIdMember, wFlags,
495                 pDispParams, pVarResult, pExcepInfo, puArgErr);
496         ITypeInfo_Release(typeinfo);
497     }
498 
499     return hr;
500 }
501 
502 static HRESULT WINAPI dictionary_putref_Item(IDictionary *iface, VARIANT *Key, VARIANT *pRetItem)
503 {
504     dictionary *This = impl_from_IDictionary(iface);
505 
506     FIXME("(%p)->(%p %p)\n", This, Key, pRetItem);
507 
508     return E_NOTIMPL;
509 }
510 
511 static HRESULT WINAPI dictionary_put_Item(IDictionary *iface, VARIANT *key, VARIANT *item)
512 {
513     dictionary *This = impl_from_IDictionary(iface);
514     struct keyitem_pair *pair;
515 
516     TRACE("(%p)->(%s %s)\n", This, debugstr_variant(key), debugstr_variant(item));
517 
518     if ((pair = get_keyitem_pair(This, key)))
519         return VariantCopyInd(&pair->item, item);
520 
521     return IDictionary_Add(iface, key, item);
522 }
523 
524 static HRESULT WINAPI dictionary_get_Item(IDictionary *iface, VARIANT *key, VARIANT *item)
525 {
526     dictionary *This = impl_from_IDictionary(iface);
527     struct keyitem_pair *pair;
528 
529     TRACE("(%p)->(%s %p)\n", This, debugstr_variant(key), item);
530 
531     if ((pair = get_keyitem_pair(This, key)))
532         VariantCopy(item, &pair->item);
533     else {
534         VariantInit(item);
535         return IDictionary_Add(iface, key, item);
536     }
537 
538     return S_OK;
539 }
540 
541 static HRESULT WINAPI dictionary_Add(IDictionary *iface, VARIANT *key, VARIANT *item)
542 {
543     dictionary *This = impl_from_IDictionary(iface);
544 
545     TRACE("(%p)->(%s %s)\n", This, debugstr_variant(key), debugstr_variant(item));
546 
547     if (get_keyitem_pair(This, key))
548         return CTL_E_KEY_ALREADY_EXISTS;
549 
550     return add_keyitem_pair(This, key, item);
551 }
552 
553 static HRESULT WINAPI dictionary_get_Count(IDictionary *iface, LONG *count)
554 {
555     dictionary *This = impl_from_IDictionary(iface);
556 
557     TRACE("(%p)->(%p)\n", This, count);
558 
559     *count = This->count;
560     return S_OK;
561 }
562 
563 static HRESULT WINAPI dictionary_Exists(IDictionary *iface, VARIANT *key, VARIANT_BOOL *exists)
564 {
565     dictionary *This = impl_from_IDictionary(iface);
566 
567     TRACE("(%p)->(%s %p)\n", This, debugstr_variant(key), exists);
568 
569     if (!exists)
570         return CTL_E_ILLEGALFUNCTIONCALL;
571 
572     *exists = get_keyitem_pair(This, key) != NULL ? VARIANT_TRUE : VARIANT_FALSE;
573     return S_OK;
574 }
575 
576 static HRESULT WINAPI dictionary_Items(IDictionary *iface, VARIANT *items)
577 {
578     dictionary *This = impl_from_IDictionary(iface);
579     struct keyitem_pair *pair;
580     SAFEARRAYBOUND bound;
581     SAFEARRAY *sa;
582     VARIANT *v;
583     HRESULT hr;
584     LONG i;
585 
586     TRACE("(%p)->(%p)\n", This, items);
587 
588     if (!items)
589         return S_OK;
590 
591     bound.lLbound = 0;
592     bound.cElements = This->count;
593     sa = SafeArrayCreate(VT_VARIANT, 1, &bound);
594     if (!sa)
595         return E_OUTOFMEMORY;
596 
597     hr = SafeArrayAccessData(sa, (void**)&v);
598     if (FAILED(hr)) {
599         SafeArrayDestroy(sa);
600         return hr;
601     }
602 
603     i = 0;
604     LIST_FOR_EACH_ENTRY(pair, &This->pairs, struct keyitem_pair, entry) {
605         VariantCopy(&v[i], &pair->item);
606         i++;
607     }
608     SafeArrayUnaccessData(sa);
609 
610     V_VT(items) = VT_ARRAY|VT_VARIANT;
611     V_ARRAY(items) = sa;
612     return S_OK;
613 }
614 
615 static HRESULT WINAPI dictionary_put_Key(IDictionary *iface, VARIANT *key, VARIANT *newkey)
616 {
617     dictionary *This = impl_from_IDictionary(iface);
618     struct keyitem_pair *pair;
619     VARIANT empty;
620     HRESULT hr;
621 
622     TRACE("(%p)->(%s %s)\n", This, debugstr_variant(key), debugstr_variant(newkey));
623 
624     if ((pair = get_keyitem_pair(This, key))) {
625         /* found existing pair for a key, add new pair with new key
626            and old item and remove old pair after that */
627 
628         hr = IDictionary_Add(iface, newkey, &pair->item);
629         if (FAILED(hr))
630             return hr;
631 
632         return IDictionary_Remove(iface, key);
633     }
634 
635     VariantInit(&empty);
636     return IDictionary_Add(iface, newkey, &empty);
637 }
638 
639 static HRESULT WINAPI dictionary_Keys(IDictionary *iface, VARIANT *keys)
640 {
641     dictionary *This = impl_from_IDictionary(iface);
642     struct keyitem_pair *pair;
643     SAFEARRAYBOUND bound;
644     SAFEARRAY *sa;
645     VARIANT *v;
646     HRESULT hr;
647     LONG i;
648 
649     TRACE("(%p)->(%p)\n", This, keys);
650 
651     if (!keys)
652         return S_OK;
653 
654     bound.lLbound = 0;
655     bound.cElements = This->count;
656     sa = SafeArrayCreate(VT_VARIANT, 1, &bound);
657     if (!sa)
658         return E_OUTOFMEMORY;
659 
660     hr = SafeArrayAccessData(sa, (void**)&v);
661     if (FAILED(hr)) {
662         SafeArrayDestroy(sa);
663         return hr;
664     }
665 
666     i = 0;
667     LIST_FOR_EACH_ENTRY(pair, &This->pairs, struct keyitem_pair, entry) {
668         VariantCopy(&v[i], &pair->key);
669         i++;
670     }
671     SafeArrayUnaccessData(sa);
672 
673     V_VT(keys) = VT_ARRAY|VT_VARIANT;
674     V_ARRAY(keys) = sa;
675     return S_OK;
676 }
677 
678 static HRESULT WINAPI dictionary_Remove(IDictionary *iface, VARIANT *key)
679 {
680     dictionary *This = impl_from_IDictionary(iface);
681     struct keyitem_pair *pair;
682 
683     TRACE("(%p)->(%s)\n", This, debugstr_variant(key));
684 
685     if (!(pair = get_keyitem_pair(This, key)))
686         return CTL_E_ELEMENT_NOT_FOUND;
687 
688     notify_remove_pair(&This->notifier, &pair->entry);
689     list_remove(&pair->entry);
690     list_remove(&pair->bucket);
691     This->count--;
692 
693     free_keyitem_pair(pair);
694     return S_OK;
695 }
696 
697 static HRESULT WINAPI dictionary_RemoveAll(IDictionary *iface)
698 {
699     dictionary *This = impl_from_IDictionary(iface);
700     struct keyitem_pair *pair, *pair2;
701 
702     TRACE("(%p)\n", This);
703 
704     if (This->count == 0)
705         return S_OK;
706 
707     notify_remove_pair(&This->notifier, NULL);
708     LIST_FOR_EACH_ENTRY_SAFE(pair, pair2, &This->pairs, struct keyitem_pair, entry) {
709         list_remove(&pair->entry);
710         list_remove(&pair->bucket);
711         free_keyitem_pair(pair);
712     }
713     This->count = 0;
714 
715     return S_OK;
716 }
717 
718 static HRESULT WINAPI dictionary_put_CompareMode(IDictionary *iface, CompareMethod method)
719 {
720     dictionary *This = impl_from_IDictionary(iface);
721 
722     TRACE("(%p)->(%d)\n", This, method);
723 
724     if (This->count)
725         return CTL_E_ILLEGALFUNCTIONCALL;
726 
727     This->method = method;
728     return S_OK;
729 }
730 
731 static HRESULT WINAPI dictionary_get_CompareMode(IDictionary *iface, CompareMethod *method)
732 {
733     dictionary *This = impl_from_IDictionary(iface);
734 
735     TRACE("(%p)->(%p)\n", This, method);
736 
737     *method = This->method;
738     return S_OK;
739 }
740 
741 static HRESULT WINAPI dictionary__NewEnum(IDictionary *iface, IUnknown **ret)
742 {
743     dictionary *This = impl_from_IDictionary(iface);
744 
745     TRACE("(%p)->(%p)\n", This, ret);
746 
747     return create_dict_enum(This, ret);
748 }
749 
750 static DWORD get_str_hash(const WCHAR *str, CompareMethod method)
751 {
752     DWORD hash = 0;
753 
754     if (str) {
755         while (*str) {
756             WCHAR ch;
757 
758             ch = (method == TextCompare || method == DatabaseCompare) ? towlower(*str) : *str;
759 
760             hash += (hash << 4) + ch;
761             str++;
762         }
763     }
764 
765     return hash % DICT_HASH_MOD;
766 }
767 
768 static DWORD get_num_hash(FLOAT num)
769 {
770     return (*((DWORD*)&num)) % DICT_HASH_MOD;
771 }
772 
773 static HRESULT get_flt_hash(FLOAT flt, LONG *hash)
774 {
775     if (isinf(flt)) {
776         *hash = 0;
777         return S_OK;
778     }
779     else if (!isnan(flt)) {
780         *hash = get_num_hash(flt);
781         return S_OK;
782     }
783 
784     /* NaN case */
785     *hash = ~0u;
786     return CTL_E_ILLEGALFUNCTIONCALL;
787 }
788 
789 static DWORD get_ptr_hash(void *ptr)
790 {
791     return PtrToUlong(ptr) % DICT_HASH_MOD;
792 }
793 
794 static HRESULT WINAPI dictionary_get_HashVal(IDictionary *iface, VARIANT *key, VARIANT *hash)
795 {
796     dictionary *This = impl_from_IDictionary(iface);
797 
798     TRACE("(%p)->(%s %p)\n", This, debugstr_variant(key), hash);
799 
800     V_VT(hash) = VT_I4;
801     switch (V_VT(key))
802     {
803     case VT_BSTR|VT_BYREF:
804     case VT_BSTR:
805         V_I4(hash) = get_str_hash(get_key_strptr(key), This->method);
806         break;
807     case VT_UI1|VT_BYREF:
808     case VT_UI1:
809         V_I4(hash) = get_num_hash(V_VT(key) & VT_BYREF ? *V_UI1REF(key) : V_UI1(key));
810         break;
811     case VT_I2|VT_BYREF:
812     case VT_I2:
813         V_I4(hash) = get_num_hash(V_VT(key) & VT_BYREF ? *V_I2REF(key) : V_I2(key));
814         break;
815     case VT_I4|VT_BYREF:
816     case VT_I4:
817         V_I4(hash) = get_num_hash(V_VT(key) & VT_BYREF ? *V_I4REF(key) : V_I4(key));
818         break;
819     case VT_UNKNOWN|VT_BYREF:
820     case VT_DISPATCH|VT_BYREF:
821     case VT_UNKNOWN:
822     case VT_DISPATCH:
823     {
824         IUnknown *src = (V_VT(key) & VT_BYREF) ? *V_UNKNOWNREF(key) : V_UNKNOWN(key);
825         IUnknown *unk = NULL;
826 
827         if (!src) {
828             V_I4(hash) = 0;
829             return S_OK;
830         }
831 
832         IUnknown_QueryInterface(src, &IID_IUnknown, (void**)&unk);
833         if (!unk) {
834             V_I4(hash) = ~0u;
835             return CTL_E_ILLEGALFUNCTIONCALL;
836         }
837         V_I4(hash) = get_ptr_hash(unk);
838         IUnknown_Release(unk);
839         break;
840     }
841     case VT_DATE|VT_BYREF:
842     case VT_DATE:
843         return get_flt_hash(V_VT(key) & VT_BYREF ? *V_DATEREF(key) : V_DATE(key), &V_I4(hash));
844     case VT_R4|VT_BYREF:
845     case VT_R4:
846         return get_flt_hash(V_VT(key) & VT_BYREF ? *V_R4REF(key) : V_R4(key), &V_I4(hash));
847     case VT_R8|VT_BYREF:
848     case VT_R8:
849         return get_flt_hash(V_VT(key) & VT_BYREF ? *V_R8REF(key) : V_R8(key), &V_I4(hash));
850     case VT_INT:
851     case VT_UINT:
852     case VT_I1:
853     case VT_I8:
854     case VT_UI2:
855     case VT_UI4:
856         V_I4(hash) = ~0u;
857         return CTL_E_ILLEGALFUNCTIONCALL;
858     default:
859         FIXME("not implemented for type %d\n", V_VT(key));
860         return E_NOTIMPL;
861     }
862 
863     return S_OK;
864 }
865 
866 static const struct IDictionaryVtbl dictionary_vtbl =
867 {
868     dictionary_QueryInterface,
869     dictionary_AddRef,
870     dictionary_Release,
871     dictionary_GetTypeInfoCount,
872     dictionary_GetTypeInfo,
873     dictionary_GetIDsOfNames,
874     dictionary_Invoke,
875     dictionary_putref_Item,
876     dictionary_put_Item,
877     dictionary_get_Item,
878     dictionary_Add,
879     dictionary_get_Count,
880     dictionary_Exists,
881     dictionary_Items,
882     dictionary_put_Key,
883     dictionary_Keys,
884     dictionary_Remove,
885     dictionary_RemoveAll,
886     dictionary_put_CompareMode,
887     dictionary_get_CompareMode,
888     dictionary__NewEnum,
889     dictionary_get_HashVal
890 };
891 
892 HRESULT WINAPI Dictionary_CreateInstance(IClassFactory *factory,IUnknown *outer,REFIID riid, void **obj)
893 {
894     dictionary *This;
895 
896     TRACE("(%p, %p, %s, %p)\n", factory, outer, debugstr_guid(riid), obj);
897 
898     *obj = NULL;
899 
900     This = heap_alloc(sizeof(*This));
901     if(!This) return E_OUTOFMEMORY;
902 
903     This->IDictionary_iface.lpVtbl = &dictionary_vtbl;
904     This->ref = 1;
905     This->method = BinaryCompare;
906     This->count = 0;
907     list_init(&This->pairs);
908     list_init(&This->notifier);
909     memset(This->buckets, 0, sizeof(This->buckets));
910 
911     init_classinfo(&CLSID_Dictionary, (IUnknown *)&This->IDictionary_iface, &This->classinfo);
912     *obj = &This->IDictionary_iface;
913 
914     return S_OK;
915 }
916