forked from WNKLER/RefTypes
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDictionaryHelp.bas
More file actions
375 lines (346 loc) · 12.4 KB
/
DictionaryHelp.bas
File metadata and controls
375 lines (346 loc) · 12.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
Attribute VB_Name = "DictionaryHelp"
Option Explicit
'https://www.cyberforum.ru/visual-basic/thread1146688.html
Private Declare PtrSafe Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As LongPtr)
Private Declare PtrSafe Function VarBstrCmp Lib "oleaut32" (ByVal bstrLeft As LongPtr, ByVal bstrRight As LongPtr, ByVal lcid As Long, ByVal dwFlags As Long) As Long
Private Declare PtrSafe Sub VariantCopy Lib "oleaut32.dll" (pvargDest As Any, pvargSrc As Any)
Private Type tLong
l As Long
End Type
Private Type tCurrency
c As Currency
End Type
#If Win64 Then
Private Const ptrSz = 8
Private Const varSz = 24
Private Const dictpFirstOffset = 48
Private Const dictpHTblOffset = 64
Private Const dictDivOffset = 72
Private Const dictlcidOffset = 80
Private Const dictSKeyOffset = 24
Private Const dictVItemOffset = 40
Private Const dictItemSize = 72
#Else
Private Const ptrSz = 4
Private Const varSz = 16
Private Const dictpFirstOffset = 28
Private Const dictpHTblOffset = 36
Private Const dictDivOffset = 40
Private Const dictlcidOffset = 48
Private Const dictSKeyOffset = 16
Private Const dictVItemOffset = 24
Private Const dictItemSize = 40
#End If
Private Type tDictDescr
lp1(4) As LongPtr
l1 As Long
lCnt As Long '24
pFirst As LongPtr '28
lp2 As LongPtr
pHTbl As LongPtr '36
ldiv As Long '40
lCmp As VbCompareMethod '44
lcid As Long '48
End Type
Private Type tDictItem
pInterface As LongPtr '0
pNext As LongPtr '4
Key As Variant '8
Item As Variant '18 24
PointerToHash As LongPtr '28 40
Reserved As Long '2C 44
End Type
Private DictItemRef() As tDictItem, DictItemRef_SA As SA1D
Private DictItemRef2() As tDictItem, DictItemRef2_SA As SA1D
Private DictDescRef() As tDictDescr, DictDescRef_SA As SA1D
Private isDictHlpRefInit As Boolean
Private Sub TestDescr()
Dim dict As New Dictionary, i&, pNext As LongPtr
InitDictHlp
dict.Add "key1", "item1"
dict.Add "key2", "item2"
dict.Add "key3", "item3"
DictDescRef_SA.pData = ObjPtr(dict)
With DictDescRef(0)
DictItemRef_SA.pData = .pFirst
Debug.Print .pFirst
For i = 2 To .lCnt
pNext = DictItemRef(0).pNext
DictItemRef_SA.pData = pNext
Debug.Print pNext
Next
End With
End Sub
Private Sub Example()
Dim dict As New Dictionary
dict.Add "key1", "item1"
dict.Add "key2", "item2"
dict.Add "key3", "item3"
' Debug.Print DictItem(dict, "key2")
' Debug.Print DictItemByIndex(dict, 2)
' Debug.Print DictKeyByIndex(dict, 3)
' Debug.Print Join(DictItems(dict), vbCr)
DictRemoveByIndex dict, 2
End Sub
Private Sub InitDictHlp()
Dim tdiTmp As tDictItem, tddTmp As tDictDescr
If isDictHlpRefInit Then Exit Sub
If IsInitialized Then Else Initialize
MakeRef DictItemRef_SA, VarPtr(DictItemRef_SA) - ptrSz, LenB(tdiTmp)
MakeRef DictItemRef2_SA, VarPtr(DictItemRef2_SA) - ptrSz, LenB(tdiTmp)
MakeRef DictDescRef_SA, VarPtr(DictDescRef_SA) - ptrSz, LenB(tddTmp)
isDictHlpRefInit = True
End Sub
' Ïîëó÷èòü ýëåìåíò ïî èíäåêñó
Function DictItemByIndex(Dic As Dictionary, ByVal Index As Long) As Variant
Dim pDesc As LongPtr, i&
If isDictHlpRefInit Then Else InitDictHlp
pDesc = ObjPtr(Dic)
If pDesc Then Else GoTo errArgum
DictDescRef_SA.pData = pDesc
With DictDescRef(0)
Select Case Index
Case 1 To .lCnt
Case Else: GoTo errArgum
End Select
DictItemRef_SA.pData = .pFirst
End With
For i = 2 To Index
DictItemRef_SA.pData = DictItemRef(0).pNext
Next
DictItemByIndex = DictItemRef(0).Item
DictItemRef_SA.pData = NullPtr
Exit Function
errArgum:
DictItemRef_SA.pData = NullPtr
Err.Raise 5, , "Bed argument value!"
End Function
' Ïîëó÷èòü êëþ÷ ïî èíäåêñó
Function DictKeyByIndex(Dic As Dictionary, ByVal Index As Long) As Variant
Dim pDesc As LongPtr, i&
If isDictHlpRefInit Then Else InitDictHlp
pDesc = ObjPtr(Dic)
If pDesc Then Else GoTo errArgum
DictDescRef_SA.pData = pDesc
With DictDescRef(0)
Select Case Index
Case 1 To .lCnt
Case Else: GoTo errArgum
End Select
DictItemRef_SA.pData = .pFirst
End With
For i = 2 To Index
DictItemRef_SA.pData = DictItemRef(0).pNext
Next
DictKeyByIndex = DictItemRef(0).Key
DictItemRef_SA.pData = NullPtr
Exit Function
errArgum:
DictItemRef_SA.pData = NullPtr
Err.Raise 5, , "Bed argument value!"
End Function
' Óäàëèòü ýëåìåíò ïî èíäåêñó
Sub DictRemoveByIndex(Dic As Dictionary, ByVal Index As Long)
Dim pDesc As LongPtr, i&
If isDictHlpRefInit Then Else InitDictHlp
pDesc = ObjPtr(Dic)
If pDesc Then Else GoTo errArgum
DictDescRef_SA.pData = pDesc
With DictDescRef(0)
Select Case Index
Case 1 To .lCnt
Case Else: GoTo errArgum
End Select
DictItemRef_SA.pData = .pFirst
End With
For i = 2 To Index
DictItemRef_SA.pData = DictItemRef(0).pNext
Next
Dic.Remove DictItemRef(0).Key
DictItemRef_SA.pData = NullPtr
Exit Sub
errArgum:
DictItemRef_SA.pData = NullPtr
Err.Raise 5, , "Bed argument value!"
End Sub
' Ïîëó÷èòü ýëåìåíò ïî êëþ÷ó. Ðåïëèêà ôóíêöèè The trick-à https://www.cyberforum.ru/visual-basic/thread1146688.html#post6040814
Private Function DictItem(Dic As Dictionary, Key As String) As Variant
Dim Hash As Long, pHTbl As LongPtr, pHItem As LongPtr, pKey As LongPtr, cmp As Long ', lcid As Long
If isDictHlpRefInit Then Else InitDictHlp
DictDescRef_SA.pData = ObjPtr(Dic)
cmp = DictDescRef(0).lCmp 'Dic.CompareMode
' lcid = DictDescRef(0).lcid ' Ïîëó÷àåì lcid
Hash = HashValVBA(DictDescRef(0), Key) ' Âû÷èñëÿåì õýø
pHTbl = DictDescRef(0).pHTbl ' Ïîëó÷àåì óêàçàòåëü íà õýø-òàáëèöó
pHItem = GetPtr(pHTbl + Hash * ptrSz) ' Ïîëó÷àåì óêàçàòåëü ýëåìåíòà â õýø-òàáëèöå
Do While pHItem ' Åñëè åñòü òàêîé ýëåìåíò
DictItemRef_SA.pData = pHItem
If StrComp(Key, DictItemRef(0).Key, cmp) = 0 Then
DictItem = DictItemRef(0).Item: Exit Function
Else: pHItem = GetPtr(pHItem + dictItemSize)
End If
Loop
DictItemRef_SA.pData = NullPtr
End Function
Private Function HashValVBA(DictDescr As tDictDescr, s As String) As Long
Dim i&, ch@, lnStr&, sTmp$
Dim res As tLong, cres As tCurrency
With DictDescr
If .lCmp = 0 Then
iMap1_SA.pData = StrPtr(s)
ElseIf .lCmp = TextCompare Then
sTmp = LCase$(s)
iMap1_SA.pData = StrPtr(sTmp)
Else: iMap1_SA.pData = StrPtr(s)
End If
lnStr = Len(s)
iMap1_SA.Count = lnStr
' Èçâðàùåíèÿ ñ Currency, ò.ê. â VB íåò UINT32 è öèêëè÷åñêîé àðèôìåòèêè
For i = 1 To lnStr
ch = iMap1(i) / 10000
cres.c = CCur(res.l) / 10000 * 17 + ch
LSet res = cres
Next
cres.c = 0: LSet cres = res
ch = cres.c * 10000
HashValVBA = ch - (Int(ch / .ldiv) * .ldiv)
End With
End Function
' Ïîëó÷èòü ñïèñîê ýëåìåíòîâ
Private Function DictItems(Dic As Dictionary) As Variant()
Dim pItem As Long, vArOut() As Variant, i As Long, Ub&
If isDictHlpRefInit Then Else InitDictHlp
DictDescRef_SA.pData = ObjPtr(Dic)
With DictDescRef(0)
Ub = .lCnt - 1
ReDim vArOut(Ub)
DictItemRef_SA.pData = .pFirst
End With
vArOut(0) = DictItemRef(0).Item
For i = 1 To Ub
DictItemRef_SA.pData = DictItemRef(0).pNext
vArOut(i) = DictItemRef(0).Item
Next
DictItemRef_SA.pData = NullPtr
DictItems = vArOut
End Function
Function DictJoinedKeys(dict As Dictionary, Optional Dlm$ = " ") As String
Dim i&, sRes$, resLen&, dlmLen&, newLen&, maxLen&, keyLen&
Dim pRes As LongPtr, pDst As LongPtr, stpInc&, sTmp$, pTmp As LongPtr
If isDictHlpRefInit Then Else InitDictHlp
dlmLen = LenB(Dlm)
DictDescRef_SA.pData = ObjPtr(dict)
With DictDescRef(0)
If .lCnt Then Else GoTo endFn
stpInc = 8
DictItemRef_SA.pData = .pFirst
sRes = DictItemRef(0).Key ': Debug.Print StrPtr(sRes)
pDst = StrPtr(sRes)
resLen = LenB(sRes)
pTmp = VarPtr(sTmp)
For i = 2 To DictDescRef(0).lCnt
DictItemRef_SA.pData = DictItemRef(0).pNext
With DictItemRef(0)
If VarType(.Key) = vbString Then
keyLen = LenB(.Key)
sRef2_SA.pData = VarPtr(.Key) + 8
Else
sTmp = .Key
keyLen = LenB(sTmp)
sRef2_SA.pData = pTmp
End If
newLen = resLen + dlmLen + keyLen
If newLen > maxLen Then
Do
maxLen = maxLen + stpInc
stpInc = stpInc * 2
Loop While newLen > maxLen
ReallocStringB sRes, maxLen ': Debug.Print StrPtr(sRes)
pRes = StrPtr(sRes)
End If
pDst = pRes + resLen
PutStrBuf pDst, Dlm
pDst = pDst + dlmLen
PutStrBuf pDst, sRef2(0)
resLen = newLen
End With
Next
End With
ReallocStringB sRes, resLen ': Debug.Print StrPtr(sRes)
MoveStr DictJoinedKeys, sRes
endFn:
DictItemRef_SA.pData = 0
DictDescRef_SA.pData = 0
End Function
Private Sub Test_DictJoinedKeys()
Dim dict As New Dictionary
Dim s$
dict.Add "key1", "item1"
dict.Add 5, "item2"
dict.Add 57.33, "item3"
dict.Add "key4", "item4"
s = DictJoinedKeys(dict)
End Sub
'' Ïîëó÷èòü ñïèñîê ýëåìåíòîâ
'Private Function Items(Dic As Dictionary) As Variant
' Dim pItem As Long, loc() As Variant, i As Long
'
' ReDim loc(Dic.Count - 1)
'
' GetMem4 ByVal ObjPtr(Dic) + dictpFirstOffset, pItem ' Óêàçàòåëü íà ïåðâûé ýëåìåíò ñïèñêà
' Do ' Ïðîõîä ïî ýëåìåíòàì ñïèñêà
' VariantCopy loc(i), ByVal pItem + &H18
' GetMem4 ByVal pItem + 4, pItem ' Ñëåäóþùèé ýëåìåíò
' i = i + 1
' Loop While pItem
'
' Items = loc
'End Function
'' Ïîëó÷èòü ýëåìåíò ïî èíäåêñó
'Private Function DictItemByIndex(Dic As Dictionary, ByVal Index As Long) As Variant
' Dim pItem As Long
' If Dic.Count = 0 Then Exit Function
'
' GetMem4 ByVal ObjPtr(Dic) + &H1C, pItem ' Óêàçàòåëü íà ïåðâûé ýëåìåíò ñïèñêà
'
' Do While CBool(Index) And pItem ' Ïðîõîä ïî ýëåìåíòàì ñïèñêà
' GetMem4 ByVal pItem + 4, pItem ' Ñëåäóþùèé ýëåìåíò
' Index = Index - 1
' Loop
'
' VariantCopy DictItemByIndex, ByVal pItem + &H18
'End Function
'' Ïîëó÷èòü ñïèñîê ýëåìåíòîâ
'Private Function Items(Dic As Dictionary) As Variant
' Dim pItem As Long, loc() As Variant, i As Long
'
' ReDim loc(Dic.Count - 1)
'
' GetMem4 ByVal ObjPtr(Dic) + dictpFirstOffset, pItem ' Óêàçàòåëü íà ïåðâûé ýëåìåíò ñïèñêà
' Do ' Ïðîõîä ïî ýëåìåíòàì ñïèñêà
' VariantCopy loc(i), ByVal pItem + &H18
' GetMem4 ByVal pItem + 4, pItem ' Ñëåäóþùèé ýëåìåíò
' i = i + 1
' Loop While pItem
'
' Items = loc
'End Function
'Private Function GetItemVBA(Dic As Dictionary, Key As String) As Variant
' Dim Hash As Long, pHTbl As LongPtr, pHItem As LongPtr, lcid As Long, pKey As LongPtr, cmp As Long
' Dim pDic As LongPtr
' pDic = ObjPtr(Dic)
' cmp = Dic.CompareMode
' CopyMemory lcid, ByVal pDic + dictlcidOffset, ptrSz ' Ïîëó÷àåì lcid
' Hash = HashValVBA(Dic, Key) ' Âû÷èñëÿåì õýø
' CopyMemory pHTbl, ByVal pDic + dictpHTblOffset, ptrSz ' Ïîëó÷àåì óêàçàòåëü íà õýø-òàáëèöó
'
' CopyMemory pHItem, ByVal pHTbl + Hash * ptrSz, ptrSz ' Ïîëó÷àåì óêàçàòåëü ýëåìåíòà â õýø-òàáëèöå
' Do While pHItem ' Åñëè åñòü òàêîé ýëåìåíò
' CopyMemory pKey, ByVal pHItem + dictSKeyOffset, ptrSz ' Ñðàâíèâàåì çíà÷åíèå êëþ÷à â òàáëèöå ñ çàäàíûì êëþ÷åì
' Select Case VarBstrCmp(StrPtr(Key), pKey, lcid, cmp)
' Case 1: VariantCopy GetItemVBA, ByVal pHItem + dictVItemOffset: Exit Function
' Case Else
' CopyMemory pHItem, ByVal pHItem + dictItemSize, ptrSz ' Ïîëó÷àåì óêàçàòåëü íà ñëåäóþùóþ çàïèñü â òàáëèöå
' End Select
' Loop
'End Function