| /* | |
| * Copyright (C) 2012 Alistair Leslie-Hughes | |
| * Copyright 2015 Nikolay Sivov for CodeWeavers | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | |
| */ | |
| #define COBJMACROS | |
| #include "config.h" | |
| #include "wine/port.h" | |
| #include <stdarg.h> | |
| #include "windef.h" | |
| #include "winbase.h" | |
| #include "ole2.h" | |
| #include "olectl.h" | |
| #include "dispex.h" | |
| #include "scrrun.h" | |
| #include "scrrun_private.h" | |
| #include "wine/debug.h" | |
| #include "wine/unicode.h" | |
| #include "wine/list.h" | |
| WINE_DEFAULT_DEBUG_CHANNEL(scrrun); | |
| #define BUCKET_COUNT 509 | |
| #define DICT_HASH_MOD 1201 | |
| /* Implementation details | |
| Dictionary contains one list that links all pairs, this way | |
| order in which they were added is preserved. Each bucket has | |
| its own list to hold all pairs in this bucket. Initially all | |
| bucket lists are zeroed and we init them once we about to add | |
| first pair. | |
| When pair is removed it's unlinked from both lists; if it was | |
| a last pair in a bucket list it stays empty in initialized state. | |
| Preserving pair order is important for enumeration, so far testing | |
| indicates that pairs are not reordered basing on hash value. | |
| */ | |
| struct keyitem_pair { | |
| struct list entry; | |
| struct list bucket; | |
| DWORD hash; | |
| VARIANT key; | |
| VARIANT item; | |
| }; | |
| typedef struct | |
| { | |
| struct provideclassinfo classinfo; | |
| IDictionary IDictionary_iface; | |
| LONG ref; | |
| CompareMethod method; | |
| LONG count; | |
| struct list pairs; | |
| struct list buckets[BUCKET_COUNT]; | |
| struct list notifier; | |
| } dictionary; | |
| struct dictionary_enum { | |
| IEnumVARIANT IEnumVARIANT_iface; | |
| LONG ref; | |
| dictionary *dict; | |
| struct list *cur; | |
| struct list notify; | |
| }; | |
| static inline dictionary *impl_from_IDictionary(IDictionary *iface) | |
| { | |
| return CONTAINING_RECORD(iface, dictionary, IDictionary_iface); | |
| } | |
| static inline struct dictionary_enum *impl_from_IEnumVARIANT(IEnumVARIANT *iface) | |
| { | |
| return CONTAINING_RECORD(iface, struct dictionary_enum, IEnumVARIANT_iface); | |
| } | |
| static inline struct list *get_bucket_head(dictionary *dict, DWORD hash) | |
| { | |
| return &dict->buckets[hash % BUCKET_COUNT]; | |
| } | |
| static inline BOOL is_string_key(const VARIANT *key) | |
| { | |
| return V_VT(key) == VT_BSTR || V_VT(key) == (VT_BSTR|VT_BYREF); | |
| } | |
| /* Only for VT_BSTR or VT_BSTR|VT_BYREF types */ | |
| static inline WCHAR *get_key_strptr(const VARIANT *key) | |
| { | |
| if (V_VT(key) == VT_BSTR) | |
| return V_BSTR(key); | |
| if (V_BSTRREF(key)) | |
| return *V_BSTRREF(key); | |
| return NULL; | |
| } | |
| /* should be used only when both keys are of string type, it's not checked */ | |
| static inline int strcmp_key(const dictionary *dict, const VARIANT *key1, const VARIANT *key2) | |
| { | |
| const WCHAR *str1, *str2; | |
| str1 = get_key_strptr(key1); | |
| str2 = get_key_strptr(key2); | |
| return dict->method == BinaryCompare ? strcmpW(str1, str2) : strcmpiW(str1, str2); | |
| } | |
| static BOOL is_matching_key(const dictionary *dict, const struct keyitem_pair *pair, const VARIANT *key, DWORD hash) | |
| { | |
| if (is_string_key(key) && is_string_key(&pair->key)) { | |
| if (hash != pair->hash) | |
| return FALSE; | |
| return strcmp_key(dict, key, &pair->key) == 0; | |
| } | |
| if ((is_string_key(key) && !is_string_key(&pair->key)) || | |
| (!is_string_key(key) && is_string_key(&pair->key))) | |
| return FALSE; | |
| /* for numeric keys only check hash */ | |
| return hash == pair->hash; | |
| } | |
| static struct keyitem_pair *get_keyitem_pair(dictionary *dict, VARIANT *key) | |
| { | |
| struct keyitem_pair *pair; | |
| struct list *head, *entry; | |
| VARIANT hash; | |
| HRESULT hr; | |
| hr = IDictionary_get_HashVal(&dict->IDictionary_iface, key, &hash); | |
| if (FAILED(hr)) | |
| return NULL; | |
| head = get_bucket_head(dict, V_I4(&hash)); | |
| if (!head->next || list_empty(head)) | |
| return NULL; | |
| entry = list_head(head); | |
| do { | |
| pair = LIST_ENTRY(entry, struct keyitem_pair, bucket); | |
| if (is_matching_key(dict, pair, key, V_I4(&hash))) return pair; | |
| } while ((entry = list_next(head, entry))); | |
| return NULL; | |
| } | |
| static HRESULT add_keyitem_pair(dictionary *dict, VARIANT *key, VARIANT *item) | |
| { | |
| struct keyitem_pair *pair; | |
| struct list *head; | |
| VARIANT hash; | |
| HRESULT hr; | |
| hr = IDictionary_get_HashVal(&dict->IDictionary_iface, key, &hash); | |
| if (FAILED(hr)) | |
| return hr; | |
| pair = heap_alloc(sizeof(*pair)); | |
| if (!pair) | |
| return E_OUTOFMEMORY; | |
| pair->hash = V_I4(&hash); | |
| VariantInit(&pair->key); | |
| VariantInit(&pair->item); | |
| hr = VariantCopyInd(&pair->key, key); | |
| if (FAILED(hr)) | |
| goto failed; | |
| hr = VariantCopyInd(&pair->item, item); | |
| if (FAILED(hr)) | |
| goto failed; | |
| head = get_bucket_head(dict, pair->hash); | |
| if (!head->next) | |
| /* this only happens once per bucket */ | |
| list_init(head); | |
| /* link to bucket list and to full list */ | |
| list_add_tail(head, &pair->bucket); | |
| list_add_tail(&dict->pairs, &pair->entry); | |
| dict->count++; | |
| return S_OK; | |
| failed: | |
| VariantClear(&pair->key); | |
| VariantClear(&pair->item); | |
| heap_free(pair); | |
| return hr; | |
| } | |
| static void free_keyitem_pair(struct keyitem_pair *pair) | |
| { | |
| VariantClear(&pair->key); | |
| VariantClear(&pair->item); | |
| heap_free(pair); | |
| } | |
| static HRESULT WINAPI dict_enum_QueryInterface(IEnumVARIANT *iface, REFIID riid, void **obj) | |
| { | |
| struct dictionary_enum *This = impl_from_IEnumVARIANT(iface); | |
| TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), obj); | |
| if (IsEqualIID(riid, &IID_IEnumVARIANT) || IsEqualIID(riid, &IID_IUnknown)) { | |
| *obj = iface; | |
| IEnumVARIANT_AddRef(iface); | |
| return S_OK; | |
| } | |
| else { | |
| WARN("interface not supported %s\n", debugstr_guid(riid)); | |
| *obj = NULL; | |
| return E_NOINTERFACE; | |
| } | |
| } | |
| static ULONG WINAPI dict_enum_AddRef(IEnumVARIANT *iface) | |
| { | |
| struct dictionary_enum *This = impl_from_IEnumVARIANT(iface); | |
| ULONG ref = InterlockedIncrement(&This->ref); | |
| TRACE("(%p)->(%u)\n", This, ref); | |
| return ref; | |
| } | |
| static ULONG WINAPI dict_enum_Release(IEnumVARIANT *iface) | |
| { | |
| struct dictionary_enum *This = impl_from_IEnumVARIANT(iface); | |
| LONG ref = InterlockedDecrement(&This->ref); | |
| TRACE("(%p)->(%u)\n", This, ref); | |
| if (!ref) { | |
| list_remove(&This->notify); | |
| IDictionary_Release(&This->dict->IDictionary_iface); | |
| heap_free(This); | |
| } | |
| return ref; | |
| } | |
| static HRESULT WINAPI dict_enum_Next(IEnumVARIANT *iface, ULONG count, VARIANT *keys, ULONG *fetched) | |
| { | |
| struct dictionary_enum *This = impl_from_IEnumVARIANT(iface); | |
| struct keyitem_pair *pair; | |
| ULONG i = 0; | |
| TRACE("(%p)->(%u %p %p)\n", This, count, keys, fetched); | |
| if (fetched) | |
| *fetched = 0; | |
| if (!count) | |
| return S_OK; | |
| while (This->cur && i < count) { | |
| pair = LIST_ENTRY(This->cur, struct keyitem_pair, entry); | |
| VariantCopy(&keys[i], &pair->key); | |
| This->cur = list_next(&This->dict->pairs, This->cur); | |
| i++; | |
| } | |
| if (fetched) | |
| *fetched = i; | |
| return i < count ? S_FALSE : S_OK; | |
| } | |
| static HRESULT WINAPI dict_enum_Skip(IEnumVARIANT *iface, ULONG count) | |
| { | |
| struct dictionary_enum *This = impl_from_IEnumVARIANT(iface); | |
| TRACE("(%p)->(%u)\n", This, count); | |
| if (!count) | |
| return S_OK; | |
| if (!This->cur) | |
| return S_FALSE; | |
| while (count--) { | |
| This->cur = list_next(&This->dict->pairs, This->cur); | |
| if (!This->cur) break; | |
| } | |
| return count == 0 ? S_OK : S_FALSE; | |
| } | |
| static HRESULT WINAPI dict_enum_Reset(IEnumVARIANT *iface) | |
| { | |
| struct dictionary_enum *This = impl_from_IEnumVARIANT(iface); | |
| TRACE("(%p)\n", This); | |
| This->cur = list_head(&This->dict->pairs); | |
| return S_OK; | |
| } | |
| static HRESULT create_dict_enum(dictionary*, IUnknown**); | |
| static HRESULT WINAPI dict_enum_Clone(IEnumVARIANT *iface, IEnumVARIANT **cloned) | |
| { | |
| struct dictionary_enum *This = impl_from_IEnumVARIANT(iface); | |
| TRACE("(%p)->(%p)\n", This, cloned); | |
| return create_dict_enum(This->dict, (IUnknown**)cloned); | |
| } | |
| static const IEnumVARIANTVtbl dictenumvtbl = { | |
| dict_enum_QueryInterface, | |
| dict_enum_AddRef, | |
| dict_enum_Release, | |
| dict_enum_Next, | |
| dict_enum_Skip, | |
| dict_enum_Reset, | |
| dict_enum_Clone | |
| }; | |
| static HRESULT create_dict_enum(dictionary *dict, IUnknown **ret) | |
| { | |
| struct dictionary_enum *This; | |
| *ret = NULL; | |
| This = heap_alloc(sizeof(*This)); | |
| if (!This) | |
| return E_OUTOFMEMORY; | |
| This->IEnumVARIANT_iface.lpVtbl = &dictenumvtbl; | |
| This->ref = 1; | |
| This->cur = list_head(&dict->pairs); | |
| list_add_tail(&dict->notifier, &This->notify); | |
| This->dict = dict; | |
| IDictionary_AddRef(&dict->IDictionary_iface); | |
| *ret = (IUnknown*)&This->IEnumVARIANT_iface; | |
| return S_OK; | |
| } | |
| static void notify_remove_pair(struct list *notifier, struct list *pair) | |
| { | |
| struct dictionary_enum *dict_enum; | |
| struct list *cur; | |
| LIST_FOR_EACH(cur, notifier) { | |
| dict_enum = LIST_ENTRY(cur, struct dictionary_enum, notify); | |
| if (!pair) | |
| dict_enum->cur = list_head(&dict_enum->dict->pairs); | |
| else if (dict_enum->cur == pair) { | |
| dict_enum->cur = list_next(&dict_enum->dict->pairs, dict_enum->cur); | |
| } | |
| } | |
| } | |
| static HRESULT WINAPI dictionary_QueryInterface(IDictionary *iface, REFIID riid, void **obj) | |
| { | |
| dictionary *This = impl_from_IDictionary(iface); | |
| TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), obj); | |
| *obj = NULL; | |
| if(IsEqualIID(riid, &IID_IUnknown) || | |
| IsEqualIID(riid, &IID_IDispatch) || | |
| IsEqualIID(riid, &IID_IDictionary)) | |
| { | |
| *obj = &This->IDictionary_iface; | |
| } | |
| else if (IsEqualIID(riid, &IID_IProvideClassInfo)) | |
| { | |
| *obj = &This->classinfo.IProvideClassInfo_iface; | |
| } | |
| else if ( IsEqualGUID( riid, &IID_IDispatchEx )) | |
| { | |
| TRACE("Interface IDispatchEx not supported - returning NULL\n"); | |
| *obj = NULL; | |
| return E_NOINTERFACE; | |
| } | |
| else if ( IsEqualGUID( riid, &IID_IObjectWithSite )) | |
| { | |
| TRACE("Interface IObjectWithSite not supported - returning NULL\n"); | |
| *obj = NULL; | |
| return E_NOINTERFACE; | |
| } | |
| else | |
| { | |
| WARN("interface %s not implemented\n", debugstr_guid(riid)); | |
| return E_NOINTERFACE; | |
| } | |
| IUnknown_AddRef((IUnknown*)*obj); | |
| return S_OK; | |
| } | |
| static ULONG WINAPI dictionary_AddRef(IDictionary *iface) | |
| { | |
| dictionary *This = impl_from_IDictionary(iface); | |
| ULONG ref = InterlockedIncrement(&This->ref); | |
| TRACE("(%p)->(%u)\n", This, ref); | |
| return ref; | |
| } | |
| static ULONG WINAPI dictionary_Release(IDictionary *iface) | |
| { | |
| dictionary *This = impl_from_IDictionary(iface); | |
| ULONG ref = InterlockedDecrement(&This->ref); | |
| TRACE("(%p)->(%u)\n", This, ref); | |
| if (!ref) { | |
| IDictionary_RemoveAll(iface); | |
| heap_free(This); | |
| } | |
| return ref; | |
| } | |
| static HRESULT WINAPI dictionary_GetTypeInfoCount(IDictionary *iface, UINT *pctinfo) | |
| { | |
| dictionary *This = impl_from_IDictionary(iface); | |
| TRACE("(%p)->(%p)\n", This, pctinfo); | |
| *pctinfo = 1; | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI dictionary_GetTypeInfo(IDictionary *iface, UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) | |
| { | |
| dictionary *This = impl_from_IDictionary(iface); | |
| TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo); | |
| return get_typeinfo(IDictionary_tid, ppTInfo); | |
| } | |
| static HRESULT WINAPI dictionary_GetIDsOfNames(IDictionary *iface, REFIID riid, LPOLESTR *rgszNames, | |
| UINT cNames, LCID lcid, DISPID *rgDispId) | |
| { | |
| dictionary *This = impl_from_IDictionary(iface); | |
| ITypeInfo *typeinfo; | |
| HRESULT hr; | |
| TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames, lcid, rgDispId); | |
| hr = get_typeinfo(IDictionary_tid, &typeinfo); | |
| if(SUCCEEDED(hr)) | |
| { | |
| hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId); | |
| ITypeInfo_Release(typeinfo); | |
| } | |
| return hr; | |
| } | |
| static HRESULT WINAPI dictionary_Invoke(IDictionary *iface, DISPID dispIdMember, REFIID riid, | |
| LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, | |
| EXCEPINFO *pExcepInfo, UINT *puArgErr) | |
| { | |
| dictionary *This = impl_from_IDictionary(iface); | |
| ITypeInfo *typeinfo; | |
| HRESULT hr; | |
| TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid), | |
| lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); | |
| hr = get_typeinfo(IDictionary_tid, &typeinfo); | |
| if(SUCCEEDED(hr)) | |
| { | |
| hr = ITypeInfo_Invoke(typeinfo, &This->IDictionary_iface, dispIdMember, wFlags, | |
| pDispParams, pVarResult, pExcepInfo, puArgErr); | |
| ITypeInfo_Release(typeinfo); | |
| } | |
| return hr; | |
| } | |
| static HRESULT WINAPI dictionary_putref_Item(IDictionary *iface, VARIANT *Key, VARIANT *pRetItem) | |
| { | |
| dictionary *This = impl_from_IDictionary(iface); | |
| FIXME("(%p)->(%p %p)\n", This, Key, pRetItem); | |
| return E_NOTIMPL; | |
| } | |
| static HRESULT WINAPI dictionary_put_Item(IDictionary *iface, VARIANT *key, VARIANT *item) | |
| { | |
| dictionary *This = impl_from_IDictionary(iface); | |
| struct keyitem_pair *pair; | |
| TRACE("(%p)->(%s %s)\n", This, debugstr_variant(key), debugstr_variant(item)); | |
| if ((pair = get_keyitem_pair(This, key))) | |
| return VariantCopyInd(&pair->item, item); | |
| return IDictionary_Add(iface, key, item); | |
| } | |
| static HRESULT WINAPI dictionary_get_Item(IDictionary *iface, VARIANT *key, VARIANT *item) | |
| { | |
| dictionary *This = impl_from_IDictionary(iface); | |
| struct keyitem_pair *pair; | |
| TRACE("(%p)->(%s %p)\n", This, debugstr_variant(key), item); | |
| if ((pair = get_keyitem_pair(This, key))) | |
| VariantCopy(item, &pair->item); | |
| else { | |
| VariantInit(item); | |
| return IDictionary_Add(iface, key, item); | |
| } | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI dictionary_Add(IDictionary *iface, VARIANT *key, VARIANT *item) | |
| { | |
| dictionary *This = impl_from_IDictionary(iface); | |
| TRACE("(%p)->(%s %s)\n", This, debugstr_variant(key), debugstr_variant(item)); | |
| if (get_keyitem_pair(This, key)) | |
| return CTL_E_KEY_ALREADY_EXISTS; | |
| return add_keyitem_pair(This, key, item); | |
| } | |
| static HRESULT WINAPI dictionary_get_Count(IDictionary *iface, LONG *count) | |
| { | |
| dictionary *This = impl_from_IDictionary(iface); | |
| TRACE("(%p)->(%p)\n", This, count); | |
| *count = This->count; | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI dictionary_Exists(IDictionary *iface, VARIANT *key, VARIANT_BOOL *exists) | |
| { | |
| dictionary *This = impl_from_IDictionary(iface); | |
| TRACE("(%p)->(%s %p)\n", This, debugstr_variant(key), exists); | |
| if (!exists) | |
| return CTL_E_ILLEGALFUNCTIONCALL; | |
| *exists = get_keyitem_pair(This, key) != NULL ? VARIANT_TRUE : VARIANT_FALSE; | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI dictionary_Items(IDictionary *iface, VARIANT *items) | |
| { | |
| dictionary *This = impl_from_IDictionary(iface); | |
| struct keyitem_pair *pair; | |
| SAFEARRAYBOUND bound; | |
| SAFEARRAY *sa; | |
| VARIANT *v; | |
| HRESULT hr; | |
| LONG i; | |
| TRACE("(%p)->(%p)\n", This, items); | |
| if (!items) | |
| return S_OK; | |
| bound.lLbound = 0; | |
| bound.cElements = This->count; | |
| sa = SafeArrayCreate(VT_VARIANT, 1, &bound); | |
| if (!sa) | |
| return E_OUTOFMEMORY; | |
| hr = SafeArrayAccessData(sa, (void**)&v); | |
| if (FAILED(hr)) { | |
| SafeArrayDestroy(sa); | |
| return hr; | |
| } | |
| i = 0; | |
| LIST_FOR_EACH_ENTRY(pair, &This->pairs, struct keyitem_pair, entry) { | |
| VariantCopy(&v[i], &pair->item); | |
| i++; | |
| } | |
| SafeArrayUnaccessData(sa); | |
| V_VT(items) = VT_ARRAY|VT_VARIANT; | |
| V_ARRAY(items) = sa; | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI dictionary_put_Key(IDictionary *iface, VARIANT *key, VARIANT *newkey) | |
| { | |
| dictionary *This = impl_from_IDictionary(iface); | |
| struct keyitem_pair *pair; | |
| VARIANT empty; | |
| HRESULT hr; | |
| TRACE("(%p)->(%s %s)\n", This, debugstr_variant(key), debugstr_variant(newkey)); | |
| if ((pair = get_keyitem_pair(This, key))) { | |
| /* found existing pair for a key, add new pair with new key | |
| and old item and remove old pair after that */ | |
| hr = IDictionary_Add(iface, newkey, &pair->item); | |
| if (FAILED(hr)) | |
| return hr; | |
| return IDictionary_Remove(iface, key); | |
| } | |
| VariantInit(&empty); | |
| return IDictionary_Add(iface, newkey, &empty); | |
| } | |
| static HRESULT WINAPI dictionary_Keys(IDictionary *iface, VARIANT *keys) | |
| { | |
| dictionary *This = impl_from_IDictionary(iface); | |
| struct keyitem_pair *pair; | |
| SAFEARRAYBOUND bound; | |
| SAFEARRAY *sa; | |
| VARIANT *v; | |
| HRESULT hr; | |
| LONG i; | |
| TRACE("(%p)->(%p)\n", This, keys); | |
| if (!keys) | |
| return S_OK; | |
| bound.lLbound = 0; | |
| bound.cElements = This->count; | |
| sa = SafeArrayCreate(VT_VARIANT, 1, &bound); | |
| if (!sa) | |
| return E_OUTOFMEMORY; | |
| hr = SafeArrayAccessData(sa, (void**)&v); | |
| if (FAILED(hr)) { | |
| SafeArrayDestroy(sa); | |
| return hr; | |
| } | |
| i = 0; | |
| LIST_FOR_EACH_ENTRY(pair, &This->pairs, struct keyitem_pair, entry) { | |
| VariantCopy(&v[i], &pair->key); | |
| i++; | |
| } | |
| SafeArrayUnaccessData(sa); | |
| V_VT(keys) = VT_ARRAY|VT_VARIANT; | |
| V_ARRAY(keys) = sa; | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI dictionary_Remove(IDictionary *iface, VARIANT *key) | |
| { | |
| dictionary *This = impl_from_IDictionary(iface); | |
| struct keyitem_pair *pair; | |
| TRACE("(%p)->(%s)\n", This, debugstr_variant(key)); | |
| if (!(pair = get_keyitem_pair(This, key))) | |
| return CTL_E_ELEMENT_NOT_FOUND; | |
| notify_remove_pair(&This->notifier, &pair->entry); | |
| list_remove(&pair->entry); | |
| list_remove(&pair->bucket); | |
| This->count--; | |
| free_keyitem_pair(pair); | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI dictionary_RemoveAll(IDictionary *iface) | |
| { | |
| dictionary *This = impl_from_IDictionary(iface); | |
| struct keyitem_pair *pair, *pair2; | |
| TRACE("(%p)\n", This); | |
| if (This->count == 0) | |
| return S_OK; | |
| notify_remove_pair(&This->notifier, NULL); | |
| LIST_FOR_EACH_ENTRY_SAFE(pair, pair2, &This->pairs, struct keyitem_pair, entry) { | |
| list_remove(&pair->entry); | |
| list_remove(&pair->bucket); | |
| free_keyitem_pair(pair); | |
| } | |
| This->count = 0; | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI dictionary_put_CompareMode(IDictionary *iface, CompareMethod method) | |
| { | |
| dictionary *This = impl_from_IDictionary(iface); | |
| TRACE("(%p)->(%d)\n", This, method); | |
| if (This->count) | |
| return CTL_E_ILLEGALFUNCTIONCALL; | |
| This->method = method; | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI dictionary_get_CompareMode(IDictionary *iface, CompareMethod *method) | |
| { | |
| dictionary *This = impl_from_IDictionary(iface); | |
| TRACE("(%p)->(%p)\n", This, method); | |
| *method = This->method; | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI dictionary__NewEnum(IDictionary *iface, IUnknown **ret) | |
| { | |
| dictionary *This = impl_from_IDictionary(iface); | |
| TRACE("(%p)->(%p)\n", This, ret); | |
| return create_dict_enum(This, ret); | |
| } | |
| static DWORD get_str_hash(const WCHAR *str, CompareMethod method) | |
| { | |
| DWORD hash = 0; | |
| if (str) { | |
| while (*str) { | |
| WCHAR ch; | |
| ch = (method == TextCompare || method == DatabaseCompare) ? tolowerW(*str) : *str; | |
| hash += (hash << 4) + ch; | |
| str++; | |
| } | |
| } | |
| return hash % DICT_HASH_MOD; | |
| } | |
| static DWORD get_num_hash(FLOAT num) | |
| { | |
| return (*((DWORD*)&num)) % DICT_HASH_MOD; | |
| } | |
| static HRESULT get_flt_hash(FLOAT flt, LONG *hash) | |
| { | |
| if (isinf(flt)) { | |
| *hash = 0; | |
| return S_OK; | |
| } | |
| else if (!isnan(flt)) { | |
| *hash = get_num_hash(flt); | |
| return S_OK; | |
| } | |
| /* NaN case */ | |
| *hash = ~0u; | |
| return CTL_E_ILLEGALFUNCTIONCALL; | |
| } | |
| static DWORD get_ptr_hash(void *ptr) | |
| { | |
| return PtrToUlong(ptr) % DICT_HASH_MOD; | |
| } | |
| static HRESULT WINAPI dictionary_get_HashVal(IDictionary *iface, VARIANT *key, VARIANT *hash) | |
| { | |
| dictionary *This = impl_from_IDictionary(iface); | |
| TRACE("(%p)->(%s %p)\n", This, debugstr_variant(key), hash); | |
| V_VT(hash) = VT_I4; | |
| switch (V_VT(key)) | |
| { | |
| case VT_BSTR|VT_BYREF: | |
| case VT_BSTR: | |
| V_I4(hash) = get_str_hash(get_key_strptr(key), This->method); | |
| break; | |
| case VT_UI1|VT_BYREF: | |
| case VT_UI1: | |
| V_I4(hash) = get_num_hash(V_VT(key) & VT_BYREF ? *V_UI1REF(key) : V_UI1(key)); | |
| break; | |
| case VT_I2|VT_BYREF: | |
| case VT_I2: | |
| V_I4(hash) = get_num_hash(V_VT(key) & VT_BYREF ? *V_I2REF(key) : V_I2(key)); | |
| break; | |
| case VT_I4|VT_BYREF: | |
| case VT_I4: | |
| V_I4(hash) = get_num_hash(V_VT(key) & VT_BYREF ? *V_I4REF(key) : V_I4(key)); | |
| break; | |
| case VT_UNKNOWN|VT_BYREF: | |
| case VT_DISPATCH|VT_BYREF: | |
| case VT_UNKNOWN: | |
| case VT_DISPATCH: | |
| { | |
| IUnknown *src = (V_VT(key) & VT_BYREF) ? *V_UNKNOWNREF(key) : V_UNKNOWN(key); | |
| IUnknown *unk = NULL; | |
| if (!src) { | |
| V_I4(hash) = 0; | |
| return S_OK; | |
| } | |
| IUnknown_QueryInterface(src, &IID_IUnknown, (void**)&unk); | |
| if (!unk) { | |
| V_I4(hash) = ~0u; | |
| return CTL_E_ILLEGALFUNCTIONCALL; | |
| } | |
| V_I4(hash) = get_ptr_hash(unk); | |
| IUnknown_Release(unk); | |
| break; | |
| } | |
| case VT_DATE|VT_BYREF: | |
| case VT_DATE: | |
| return get_flt_hash(V_VT(key) & VT_BYREF ? *V_DATEREF(key) : V_DATE(key), &V_I4(hash)); | |
| case VT_R4|VT_BYREF: | |
| case VT_R4: | |
| return get_flt_hash(V_VT(key) & VT_BYREF ? *V_R4REF(key) : V_R4(key), &V_I4(hash)); | |
| case VT_R8|VT_BYREF: | |
| case VT_R8: | |
| return get_flt_hash(V_VT(key) & VT_BYREF ? *V_R8REF(key) : V_R8(key), &V_I4(hash)); | |
| case VT_INT: | |
| case VT_UINT: | |
| case VT_I1: | |
| case VT_I8: | |
| case VT_UI2: | |
| case VT_UI4: | |
| V_I4(hash) = ~0u; | |
| return CTL_E_ILLEGALFUNCTIONCALL; | |
| default: | |
| FIXME("not implemented for type %d\n", V_VT(key)); | |
| return E_NOTIMPL; | |
| } | |
| return S_OK; | |
| } | |
| static const struct IDictionaryVtbl dictionary_vtbl = | |
| { | |
| dictionary_QueryInterface, | |
| dictionary_AddRef, | |
| dictionary_Release, | |
| dictionary_GetTypeInfoCount, | |
| dictionary_GetTypeInfo, | |
| dictionary_GetIDsOfNames, | |
| dictionary_Invoke, | |
| dictionary_putref_Item, | |
| dictionary_put_Item, | |
| dictionary_get_Item, | |
| dictionary_Add, | |
| dictionary_get_Count, | |
| dictionary_Exists, | |
| dictionary_Items, | |
| dictionary_put_Key, | |
| dictionary_Keys, | |
| dictionary_Remove, | |
| dictionary_RemoveAll, | |
| dictionary_put_CompareMode, | |
| dictionary_get_CompareMode, | |
| dictionary__NewEnum, | |
| dictionary_get_HashVal | |
| }; | |
| HRESULT WINAPI Dictionary_CreateInstance(IClassFactory *factory,IUnknown *outer,REFIID riid, void **obj) | |
| { | |
| dictionary *This; | |
| TRACE("(%p, %p, %s, %p)\n", factory, outer, debugstr_guid(riid), obj); | |
| *obj = NULL; | |
| This = heap_alloc(sizeof(*This)); | |
| if(!This) return E_OUTOFMEMORY; | |
| This->IDictionary_iface.lpVtbl = &dictionary_vtbl; | |
| This->ref = 1; | |
| This->method = BinaryCompare; | |
| This->count = 0; | |
| list_init(&This->pairs); | |
| list_init(&This->notifier); | |
| memset(This->buckets, 0, sizeof(This->buckets)); | |
| init_classinfo(&CLSID_Dictionary, (IUnknown *)&This->IDictionary_iface, &This->classinfo); | |
| *obj = &This->IDictionary_iface; | |
| return S_OK; | |
| } |