-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathAry.cls
560 lines (243 loc) · 11.9 KB
/
Ary.cls
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
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
END
Attribute VB_Name = "Ary"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Option Explicit
#If Win64 Then
Const cSizeOfVariant& = 2 + 6 + 16 '24
Const cSizeOfPointer& = 8
Const cNullPointer^ = 0
#Else
Const cSizeOfVariant& = 2 + 6 + 8 '16
Const cSizeOfPointer& = 4
Const cNullPointer& = 0
#End If
' win API ---------
Private Declare PtrSafe Sub MoveMemory Lib "kernel32" Alias "RtlMoveMemory" (ByRef dst_ As Any, ByRef src_ As Any, ByVal size_&)
Private Declare PtrSafe Function SafeArrayCreate Lib "oleaut32" (ByVal varType_%, ByVal cDims_&, ByRef rgsabound_ As Any) As LongPtr
Private Declare PtrSafe Function SafeArrayCreateVector Lib "oleaut32" (ByVal varType_%, ByVal lLbound_&, ByVal cElements_&) As LongPtr
Private Declare PtrSafe Function SafeArrayDestroy Lib "oleaut32" (ByRef safeArray_ As Any) As Long
Private Declare PtrSafe Function SafeArrayGetDim Lib "oleaut32" (ByRef safeArray_ As Any) As Long
Private Declare PtrSafe Function SafeArrayGetLBound Lib "oleaut32" (ByRef safeArray_ As Any, ByVal nDim_ As Long, ByRef out_lbound_ As Long) As Long
Private Declare PtrSafe Function SafeArrayGetUBound Lib "oleaut32" (ByRef safeArray_ As Any, ByVal nDim_ As Long, ByRef out_ubound_ As Long) As Long
Private Declare PtrSafe Function SafeArrayGetElemsize Lib "oleaut32" (ByRef safeArray_ As Any) As Long
Private Declare PtrSafe Function SafeArrayAccessData Lib "oleaut32" (ByRef safeArray_ As Any, ByRef out_pvData_ As LongPtr) As Long
Private Declare PtrSafe Function SafeArrayUnaccessData Lib "oleaut32" (ByRef safeArray_ As Any) As Long
Private Declare PtrSafe Function VariantCopyInd Lib "oleaut32" (ByRef dst_ As Any, ByRef src_ As Any) As Long
'--------------------
Const cVarRef& = &H4000
'構造体定義 ============================================================
' SafeArray 内部レイアウト
Private Type SafeArrayStruct
cDims As Integer
fFeatures As Integer
cbElements As Long '1要素のバイトサイズ
cLocks As Long 'ロック数。こちらでカウントアップしてしまうと、VBA側から操作できなくなる。
pvData As LongPtr 'PVOID
'SafeArrayBound rgsabound[ cDims ];
End Type
Private Type SafeArrayBound
Elements As Long '要素数
BaseIndex As Long ' LBound()
End Type
' Variant 内部レイアウト
Private Type VariantStruct
varType As Integer
reserve0 As Integer
reserve1 As Integer
reserve2 As Integer
pEntity0 As LongPtr
pEntity1 As LongPtr
End Type
'変則的 Variant 内部レイアウト
Private Type VariantStructWithCollection
varType As Integer '内部型 | vbarray
reserve0 As Integer
serialLength As Long ' reserve1 As Integer + reserve2 As Integer
'次元を無視した通し個数。Variant の予約部分を使用する。一抹の不安はある笑
pEntity0 As LongPtr
dimDefs As Collection 'pEntity1 As LongPtr
'メモリ配置としてはオブジェクトはポインターと同等なので、Nothing は cNullPointer と同等
End Type
' VT_RECORD = 36
'メンバ宣言 ============================================================
'配列保持
Private arr_ As VariantStructWithCollection ' Variant 型変数に MoveMemory できるようにこのメモリレイアウトで保持している。
'関数定義 ============================================================
'オブジェクトインスタンスの新規作成
Public Function CNew() As Ary
Set CNew = New Ary
End Function
'配列確保 ------------------------------------------------
'一次元配列 - - - - -
'一次元配列を確保(長さを指定)
Public Function Alloc(length_&, Optional varType_ As VbVarType = vbVariant) As Ary
Set Alloc = Me
Free
arr_.varType = varType_ Or vbArray
arr_.pEntity0 = SafeArrayCreateVector(varType_, 0&, length_)
arr_.serialLength = length_
End Function
'一次元配列確保(先頭添え字と長さを指定)
Public Function AllocBound(baseIndex_&, length_&, Optional varType_ As VbVarType = vbVariant) As Ary
Set AllocBound = Me
Free
arr_.varType = varType_ Or vbArray
arr_.pEntity0 = SafeArrayCreateVector(varType_, baseIndex_, length_)
arr_.serialLength = length_
End Function
'多次元配列 - - - - -
'多次元配列1要素の長さを宣言する。配列確保するのにコレクション作るとか本末転倒感…。
Public Function DimDef(length_&) As Ary
Set DimDef = Me
Free
If arr_.dimDefs Is Nothing Then Set arr_.dimDefs = New Collection
arr_.dimDefs.Add Array(0&, length_)
End Function
'多次元配列1要素の先頭添え字と長さを宣言する。
Public Function DimDefBound(baseIndex_&, length_&) As Ary
Set DimDefBound = Me
Free
If arr_.dimDefs Is Nothing Then Set arr_.dimDefs = New Collection
arr_.dimDefs.Add Array(baseIndex_, length_)
End Function
'多次元要素の宣言をもとに、多次元配列を確保する。
Public Function DimAlloc(Optional varType_ As VbVarType = vbVariant) As Ary
Set DimAlloc = Me
If arr_.dimDefs Is Nothing Then Exit Function
'多次元要素の宣言から safeArray 確保用構造体を生成する。
Dim safeArrayBounds_() As SafeArrayBound
ReDim safeArrayBounds_(arr_.dimDefs.Count - 1)
Dim totalLength_&: totalLength_ = 1 '掛け算のベースなので
Dim dimDefine_, i&
For Each dimDefine_ In arr_.dimDefs
safeArrayBounds_(i).BaseIndex = dimDefine_(0)
safeArrayBounds_(i).Elements = dimDefine_(1)
totalLength_ = totalLength_ * dimDefine_(1)
i = i + 1
Next
Set arr_.dimDefs = Nothing '宣言を開放
' safeArray を確保する。
arr_.varType = varType_ Or vbArray
arr_.pEntity0 = SafeArrayCreate(varType_, i, safeArrayBounds_(0))
arr_.serialLength = totalLength_
End Function
' -----------------------------------------------------------
'コピー/ムーブ ----------------------------
' Variant に格納された動的配列を Ary に移動する。
' 移動元には Empty が入る。
Public Function MoveFrom(ByRef ref_array_) As Ary '調整中
'Private Function MoveFrom(ByRef ref_array_) As Ary
Set MoveFrom = Me
Free
'移動先へコピーする。
MoveMemory arr_, ref_array_, cSizeOfVariant
If arr_.varType And cVarRef Then Exit Function '引数の元が Variant 以外で参照になっている場合など エラー処理すべし
'他にも、静的配列ならはじく、配列変数(4bytes)を渡されたか、など必要かも
arr_.serialLength = calcurateSerialLength_(Dimension)
'移動元を Empty にし、そちら側で開放されないようにする。
MoveMemory ref_array_, Empty, cSizeOfVariant
End Function
'配列をコピーする。
Public Function CopyFrom(array_) As Ary
Set CopyFrom = Me
Free
VariantCopyInd arr_, array_
' VariantCopy arr_, array_
arr_.serialLength = calcurateSerialLength_(Dimension - 1)
End Function
'再帰的に全要素数を求める。
Private Function calcurateSerialLength_(dimension_&) As Long
calcurateSerialLength_ = Length(dimension_)
If dimension_ = 0 Then Exit Function
calcurateSerialLength_ = calcurateSerialLength_ * calcurateSerialLength_(dimension_ - 1)
End Function
' ------------------------------------------------------------
'解放 --------------------------------------------------------
'配列の解放
Public Function Free() As Ary
Set Free = Me
If arr_.pEntity0 = cNullPointer Then Exit Function
'配列の破棄
SafeArrayDestroy ByVal arr_.pEntity0
' SafeArray のクリア
Dim blankArr_ As VariantStructWithCollection
arr_ = blankArr_ '要素宣言も解放される
End Function
Private Sub Class_Terminate()
Free
End Sub
' ---------------------------------------------------
'情報取得 -------------------------------------------
'次元ごとの要素個数を返す。次元は 0 からカウントする。次元を省略した場合、0 次元を指定したとみなす。
Public Property Get Length(Optional dimension_&) As Long
' If IsBlank Then Exit Property 'エラーが出るほうがいいか
SafeArrayGetUBound ByVal arr_.pEntity0, dimension_ + 1, out_ubound_:=Length
Length = Length + 1
End Property
Public Property Get BaseIndex(Optional dimension_&) As Long
' If IsBlank Then Exit Property 'エラーが出るほうがいいか
SafeArrayGetLBound ByVal arr_.pEntity0, dimension_ + 1, out_lbound_:=BaseIndex
End Property
'次元の数を返す。
Public Property Get Dimension() As Long
If IsBlank Then Exit Property
Dimension = SafeArrayGetDim(ByVal arr_.pEntity0)
End Property
'全要素数を返す。
Public Property Get Count() As Long
Count = arr_.serialLength
End Property
'未初期化の配列なら真が帰る。
Public Property Get IsBlank() As Boolean
IsBlank = (arr_.pEntity0 = 0)
End Property
' --------------------------------------------------------
'要素へのアクセス --------------------------------------------
'ary_.Item(0) のように使用することを想定している。
'配列の参照を返し、要素アクセスに配列コピーが生じないようにしている。
' ただし、otherArr_ = ary_.Item のように外部のバリアント変数などに渡してはいけない。
' Ary クラスが破棄されると配列本体も破棄され、そのあと参照を通してアクセスするとエクセルが落ちる。どうしたもんかね…。
Public Property Get Item() As Variant
Attribute Item.VB_UserMemId = 0
' SafeArray 参照 Varriant 構造体の構築
Dim v_ As VariantStructWithCollection
v_.varType = arr_.varType Or cVarRef
v_.pEntity0 = VarPtr(arr_.pEntity0)
'参照構造体を返す。
MoveMemory Item, v_, cSizeOfVariant
End Property
'次元を無視した通しインデックスを指定して要素を取得する。取得できた場合は真、できなかった場合は偽を返す。
Public Function Acquire(serialIndex_&, ByRef out_Item_) As Boolean
If serialIndex_ >= Count Then out_Item_ = Empty: Exit Function
'配列をロックし、要素のアドレスを取得する。
Dim pvData_ As LongPtr
SafeArrayAccessData ByVal arr_.pEntity0, out_pvData_:=pvData_
'要素をいったん Variant に参照格納し、それを型変換して戻り値にセットする。直接渡せないかな…
Dim v_ As VariantStruct
v_.varType = arr_.varType And (Not vbArray) Or cVarRef
v_.pEntity0 = pvData_ + serialIndex_ * SafeArrayGetElemsize(ByVal arr_.pEntity0)
VariantCopyInd out_Item_, v_
'ロックを解除する。
SafeArrayUnaccessData ByVal arr_.pEntity0
Acquire = True
End Function
' ------------------------------------------------------------
' 操作 -------------------------------------------------------
'転置
Private Function Transpose() As Ary
Set Transpose = Me
End Function
' ------------------------------------------------------------
Public Function NewEnum() As IEnumVARIANT
Attribute NewEnum.VB_UserMemId = -4
' Set NewEnum = Enumerable(Me).NewEnum と同じ
Dim eop_ As New EnumOperatorProcs
Set eop_.SourceIterator = Me
Set NewEnum = xCom.CreateEnumVariant(Delegate.CNew(eop_, "OpIteratorAry"))
End Function