Skip to content

Commit 10ea2bc

Browse files
committed
add BaseIndex requested feature + Enumerator fix on tB beta 416
1 parent 8a8bc12 commit 10ea2bc

File tree

4 files changed

+201
-95
lines changed

4 files changed

+201
-95
lines changed

ArrayListLib/Sources/ArrayList.twin

Lines changed: 85 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,28 @@ Public Class ArrayList
77
Private pItems() As Variant
88
Private pIndex As Long
99
Private pVersion As Long
10+
Private pBaseIndex As Long
11+
12+
Public Sub New(Optional ByVal Capacity As Long = 2, Optional ByVal BaseIndex As Long = 0)
13+
ReDim pItems(0 To Capacity - 1)
14+
pBaseIndex = BaseIndex
15+
End Sub
1016

1117
[ DefaultMember ]
1218
Public Property Get Item(ByVal Index As Long) As Variant Implements IArrayList.Item
13-
If Index < 0 Or Index >= pIndex Then Err.Raise 9
14-
Return pItems(Index)
19+
If Index < pBaseIndex Or Index >= pIndex + pBaseIndex Then Err.Raise 9
20+
Return pItems(Index - pBaseIndex)
1521
End Property
1622

1723
Public Property Let Item(ByVal Index As Long, Value As Variant) Implements IArrayList.Item
18-
If Index < 0 Or Index >= pIndex Then Err.Raise 9
19-
pItems(Index) = Value
24+
If Index < pBaseIndex Or Index >= pIndex + pBaseIndex Then Err.Raise 9
25+
pItems(Index - pBaseIndex) = Value
2026
pVersion += 1
2127
End Property
2228

2329
Public Property Set Item(ByVal Index As Long, Value As Variant) Implements IArrayList.Item
24-
If Index < 0 Or Index >= pIndex Then Err.Raise 9
25-
Set pItems(Index) = Value
30+
If Index < pBaseIndex Or Index >= pIndex + pBaseIndex Then Err.Raise 9
31+
Set pItems(Index - pBaseIndex) = Value
2632
pVersion += 1
2733
End Property
2834

@@ -35,6 +41,9 @@ Public Class ArrayList
3541
pVersion += 1
3642
End Property
3743

44+
Public Property Get BaseIndex() As Long Implements IArrayList.BaseIndex: Return pBaseIndex: End Property
45+
46+
Public Property Let BaseIndex(Value As Long) Implements IArrayList.BaseIndex: pBaseIndex = Value: pVersion += 1: End Property
3847
[ Hidden ]
3948
Public Property Get Version() As Long Implements IArrayList.Version: Return pVersion: End Property
4049

@@ -53,7 +62,7 @@ Public Class ArrayList
5362
[ Hidden ]
5463
[ Description ("CAUTION: Limit usage only in For Each In .Items calls, do NOT assign the return value of .Items to another variable unless you know what you're doing.") ]
5564
Public Function Items() As Variant() Implements IArrayList.Items
56-
/* Returns a Variant Array pointing to the same memory as the internal array of this list
65+
/* Returns a 0-based Variant Array pointing to the same memory as the internal array of this list
5766
* without increasing the reference count of byref elements within the list.
5867
* If you assign this array to a variable in your code, you must remove the reference
5968
* before it goes out of scope to prevent double deallocation of byref values.
@@ -74,7 +83,7 @@ Public Class ArrayList
7483
Public Function Add(Value As Variant) As Long Implements IArrayList.Add
7584
If pIndex > UBound(pItems) Then GrowCapacity 1
7685
If IsObject(Value) Then Set pItems(pIndex) = Value Else pItems(pIndex) = Value
77-
Add = pIndex
86+
Add = pIndex + pBaseIndex
7887
pIndex += 1
7988
pVersion += 1
8089
End Function
@@ -89,7 +98,8 @@ Public Class ArrayList
8998

9099
[ Description ("Creates a shallow copy of this ArrayList.") ]
91100
Public Function Clone() As Variant Implements IArrayList.Clone
92-
Dim Target As New ArrayList
101+
Dim Target As ArrayList
102+
Set Target = New ArrayList(BaseIndex:=pBaseIndex)
93103
C2IArrayList(Target).CloneTo Target, C2IArrayList(Me), 0, pIndex
94104
Return Target
95105
End Function
@@ -138,13 +148,41 @@ Public Class ArrayList
138148
End Sub
139149

140150
[ Enumerator ]
151+
[ Description ("When using this enumerator for more advanced usage other than regular For Each usage, Index is always 0-based regardless of the current BaseIndex value in this ArrayList.") ]
141152
Public Function GetEnumerator(Optional ByVal Index As Long = 0, Optional ByVal GetCount As Variant, Optional ByVal GetStep As Long = 1, Optional ByRef ThisEnumerator As IEnumerator) As stdole.IUnknown Implements IArrayList.GetEnumerator
153+
Static mEnumerator As Enumerator, mEnumeratorB As Enumerator, mVer As Long, mVerB As Long
142154
If IsMissing(GetCount) Then GetCount = pIndex - Index
143-
Return New Enumerator(pItems, Index, GetCount, GetStep, ThisEnumerator)
155+
If GetCount > 0 Then
156+
If mEnumerator Is Nothing Then
157+
Set mEnumerator = New Enumerator(pItems, Index, GetCount, GetStep, ThisEnumerator)
158+
mVer = pVersion
159+
Else
160+
If pVersion <> mVer Then
161+
mEnumerator.Bind pItems, Index, GetCount, GetStep
162+
mVer = pVersion
163+
Else
164+
If Not mEnumerator.IsAvailable Then
165+
If mEnumeratorB Is Nothing Then
166+
Set mEnumeratorB = New Enumerator(pItems, Index, GetCount, GetStep, ThisEnumerator)
167+
mVerB = pVersion
168+
ElseIf pVersion <> mVerB Then
169+
mEnumeratorB.Bind pItems, Index, GetCount, GetStep
170+
mVerB = pVersion
171+
End If
172+
Set ThisEnumerator = mEnumeratorB
173+
Return mEnumeratorB
174+
End If
175+
End If
176+
End If
177+
Set ThisEnumerator = mEnumerator
178+
Return mEnumerator
179+
End If
144180
End Function
145181

146182
[ Description ("Returns the index of a particular item. Returns -1 if the item isn't in the list.") ]
147-
Public Function IndexOf(Value As Variant, Optional ByVal Index As Long = 0, Optional ByVal GetCount As Variant) As Long Implements IArrayList.IndexOf
183+
Public Function IndexOf(Value As Variant, Optional ByVal Index As Variant, Optional ByVal GetCount As Variant) As Long Implements IArrayList.IndexOf
184+
If IsMissing(Index) Then Index = pBaseIndex
185+
Index = CLng(Index - pBaseIndex)
148186
If IsMissing(GetCount) Then GetCount = pIndex - Index
149187
If Index > pIndex Or GetCount < 0 Or Index > pIndex - GetCount Then Err.Raise 9
150188
GetCount = Index + GetCount - 1
@@ -153,23 +191,24 @@ Public Class ArrayList
153191
Select Case vt
154192
Case vbNull
155193
For i = Index To GetCount
156-
If IsNull(pItems(i)) Then Return i
194+
If IsNull(pItems(i)) Then Return i + pBaseIndex
157195
Next i
158196
Case vbObject
159197
For i = Index To GetCount
160-
If VarType(pItems(i)) = vt AndAlso ObjPtr(pItems(i)) = ObjPtr(Value) Then Return i
198+
If VarType(pItems(i)) = vt AndAlso ObjPtr(pItems(i)) = ObjPtr(Value) Then Return i + pBaseIndex
161199
Next i
162200
Case Else
163201
For i = Index To GetCount
164-
If VarType(pItems(i)) = vt AndAlso pItems(i) = Value Then Return i
202+
If VarType(pItems(i)) = vt AndAlso pItems(i) = Value Then Return i + pBaseIndex
165203
Next i
166204
End Select
167205
Return -1
168206
End Function
169207

170208
[ Description ("Returns the last index of a particular item. Returns -1 if the item isn't in the list.") ]
171209
Public Function LastIndexOf(ByRef Value As Variant, Optional ByVal Index As Variant, Optional ByVal GetCount As Variant) As Long Implements IArrayList.LastIndexOf
172-
If IsMissing(Index) Then Index = pIndex - 1
210+
If IsMissing(Index) Then Index = pIndex + pBaseIndex - 1
211+
Index = CLng(Index - pBaseIndex)
173212
If IsMissing(GetCount) Then GetCount = Index + 1
174213
If Index >= pIndex Or GetCount > Index + 1 Then Err.Raise 9
175214
GetCount = Index - (GetCount - 1)
@@ -178,36 +217,37 @@ Public Class ArrayList
178217
Select Case vt
179218
Case vbNull
180219
For i = Index To GetCount Step -1
181-
If IsNull(pItems(i)) Then Return i
220+
If IsNull(pItems(i)) Then Return i + pBaseIndex
182221
Next i
183222
Case vbObject
184223
For i = Index To GetCount Step -1
185-
If VarType(pItems(i)) = vt AndAlso ObjPtr(pItems(i)) = ObjPtr(Value) Then Return i
224+
If VarType(pItems(i)) = vt AndAlso ObjPtr(pItems(i)) = ObjPtr(Value) Then Return i + pBaseIndex
186225
Next i
187226
Case Else
188227
For i = Index To GetCount Step -1
189-
If VarType(pItems(i)) = vt AndAlso pItems(i) = Value Then Return i
228+
If VarType(pItems(i)) = vt AndAlso pItems(i) = Value Then Return i + pBaseIndex
190229
Next i
191230
End Select
192231
Return -1
193232
End Function
194233

195234
[ Description ("Inserts value into the list at position Index. Index must be non-negative and less than or equal to the number of elements in the list. If Index equals the number of items in the list, then value is appended to the end.") ]
196235
Public Sub Insert(ByVal Index As Long, Value As Variant) Implements IArrayList.Insert
197-
ReserveSpaceForInsert Index, 1
198-
If IsObject(Value) Then Set pItems(Index) = Value Else pItems(Index) = Value
236+
ReserveSpaceForInsert Index - pBaseIndex, 1
237+
If IsObject(Value) Then Set pItems(Index - pBaseIndex) = Value Else pItems(Index - pBaseIndex) = Value
199238
pIndex += 1
200239
pVersion += 1
201240
End Sub
202241

203242
[ Description ("Removes an item from the list.") ]
204243
Public Sub Remove(Value As Variant) Implements IArrayList.Remove
205244
Dim Index As Long = IndexOf(Value)
206-
If Index >= 0 Then RemoveAt Index
245+
If Index >= pBaseIndex Then RemoveAt Index
207246
End Sub
208247

209248
[ Description ("Removes the item at Index position.") ]
210249
Public Sub RemoveAt(ByVal Index As Long) Implements IArrayList.RemoveAt
250+
Index = Index - pBaseIndex
211251
If Index < 0 Or Index >= pIndex Then Err.Raise 9
212252
pItems(Index) = Empty
213253
If Index < pIndex - 1 Then VBA.vbaCopyBytes (pIndex - Index - 1) * VARIANT_SIZE, VarPtr(pItems(Index)), VarPtr(pItems(Index + 1))
@@ -218,6 +258,7 @@ Public Class ArrayList
218258

219259
Public Sub RemoveRange(ByVal Index As Long, ByVal GetCount As Long) Implements IArrayList.RemoveRange
220260
Dim i As Long
261+
Index = Index - pBaseIndex
221262
If Index < 0 Or GetCount < 0 Or (pIndex - Index < GetCount) Then Err.Raise 9
222263
For i = 0 To GetCount - 1
223264
pItems(Index + i) = Empty
@@ -228,30 +269,33 @@ Public Class ArrayList
228269
pVersion += 1
229270
End Sub
230271

231-
Public Sub Reverse(Optional ByVal Index As Long = 0, Optional ByVal GetCount As Variant) Implements IArrayList.Reverse
272+
Public Sub Reverse(Optional ByVal Index As Variant, Optional ByVal GetCount As Variant) Implements IArrayList.Reverse
273+
If IsMissing(Index) Then Index = pBaseIndex
274+
Index = CLng(Index) - pBaseIndex
232275
If IsMissing(GetCount) Then GetCount = pIndex - Index
233276
If Index < 0 Or GetCount < 0 Or (pIndex - Index < GetCount) Then Err.Raise 9
234277
Dim i As Long, p0 As LongPtr = VarPtr(pItems(0)), iMax As Long = GetCount \ 2
235278
For i = 0 To iMax
236-
VSwap p0 + (Index + i) * VARIANT_SIZE, p0 + CLngPtr(Index + (GetCount - 1) - i) * VARIANT_SIZE
279+
VSwap p0 + CLngPtr(Index + i) * VARIANT_SIZE, p0 + CLngPtr(Index + (GetCount - 1) - i) * VARIANT_SIZE
237280
Next i
238281
pVersion += 1
239282
End Sub
240283

241284
Public Function ToArray() As Variant() Implements IArrayList.ToArray
242285
Dim t() As Variant
243286
If pIndex = 0 Then Return Array()
244-
ReDim t(0 To pIndex - 1)
245-
VariantArrayClone VarPtr(t(0)), VarPtr(pItems(0)), pIndex
287+
ReDim t(pBaseIndex To pIndex + pBaseIndex - 1)
288+
VariantArrayClone VarPtr(t(pBaseIndex)), VarPtr(pItems(0)), pIndex
246289
ReassignArrayTo ToArray, t
247290
End Function
248291

249292
Public Sub AddRange(Target As Variant) Implements IArrayList.AddRange
250-
InsertRange pIndex, Target
293+
InsertRange pIndex + pBaseIndex, Target
251294
End Sub
252295

253296
Public Sub InsertRange(ByVal Index As Long, Target As Variant) Implements IArrayList.InsertRange
254297
Dim v As Variant, GetCount As Long
298+
Index -= pBaseIndex
255299
Select Case VarType(Target)
256300
Case vbArray + vbVariant
257301
GetCount = (UBound(Target) - LBound(Target)) + 1
@@ -282,13 +326,14 @@ Public Class ArrayList
282326
End Sub
283327

284328
Public Function GetRange(ByVal Index As Long, ByVal GetCount As Long) As IListRange Implements IArrayList.GetRange
285-
If Index < 0 Or GetCount < 0 Or pIndex - Index < GetCount Then Err.Raise 9
329+
If Index < pBaseIndex Or GetCount < 0 Or pIndex - (Index - pBaseIndex) < GetCount Then Err.Raise 9
286330
Dim r As New ListRange
287331
Set GetRange = r.Bind(Me, Index, GetCount)
288332
End Function
289333

290334
Public Sub SetRange(ByVal Index As Long, Target As Variant) Implements IArrayList.SetRange
291335
Dim v As Variant, GetCount As Long
336+
Index -= pBaseIndex
292337
Select Case VarType(Target)
293338
Case vbArray + vbVariant
294339
GetCount = (UBound(Target) - LBound(Target)) + 1
@@ -318,12 +363,14 @@ Public Class ArrayList
318363
/* [ CompilerOptions ("+llvm,+optimize") ] */
319364
[ ArrayBoundsChecks (False) ]
320365
[ IntegerOverflowChecks (False) ]
321-
Public Sub Sort(Optional ByVal Index As Long = 0, Optional ByVal GetCount As Variant, Optional Comparer As IComparer = Nothing) Implements IArrayList.Sort
366+
Public Sub Sort(Optional ByVal Index As Variant, Optional ByVal GetCount As Variant, Optional Comparer As IComparer = Nothing) Implements IArrayList.Sort
367+
If IsMissing(Index) Then Index = pBaseIndex
368+
Index -= pBaseIndex
322369
If IsMissing(GetCount) Then GetCount = pIndex - Index
323370
If Comparer IsNot Nothing Then
324-
QuickSort Index, Index + GetCount - 1, Comparer
371+
QuickSort CLng(Index), Index + GetCount - 1, Comparer
325372
Else
326-
QuickSortV2 Index, Index + GetCount - 1
373+
QuickSortV2 CLng(Index), Index + GetCount - 1
327374
End If
328375
pVersion += 1
329376
End Sub
@@ -334,13 +381,14 @@ Public Class ArrayList
334381
[ Description ("Searches a section of a sorted list. Returns the index of the given value in the list. If not found, returns a negative integer. Use the bitwise operator (Not) to get the index of the first element larger than this one, if any.") ]
335382
Public Function BinarySearch(ByVal Index As Long, ByVal GetCount As Long, Value As Variant, Optional ByRef Comparer As IComparer = Nothing) As Long Implements IArrayList.BinarySearch
336383
Dim lo As Long, hi As Long, i As Long
384+
Index -= pBaseIndex
337385
lo = Index
338386
hi = Index + GetCount - 1
339387
If Comparer IsNot Nothing Then
340388
Do While (lo <= hi)
341389
i = lo + ((hi - lo) \ 2)
342390
Select Case Comparer.Compare(pItems(i), Value)
343-
Case 0: Return i
391+
Case 0: Return i + pBaseIndex
344392
Case Is < 0: lo = i + 1
345393
Case Else: hi = i - 1
346394
End Select
@@ -349,37 +397,33 @@ Public Class ArrayList
349397
Do While (lo <= hi)
350398
i = lo + ((hi - lo) \ 2)
351399
Select Case pItems(i)
352-
Case Value: Return i
400+
Case Value: Return i + pBaseIndex
353401
Case Is < Value: lo = i + 1
354402
Case Else: hi = i - 1
355403
End Select
356404
Loop
357405
End If
358-
Return Not lo
406+
Return Not (lo + pBaseIndex)
359407
End Function
360408

361409
#Region "HIDDEN METHODS IN MSCORLIB"
362410
[ Hidden ]
363411
Public Function IndexOf_2(ByRef Value As Variant, ByVal Index As Long, ByVal GetCount As Long) As Long: Return IndexOf(Value, Index, GetCount): End Function
364412
[ Hidden ]
365-
Public Function IndexOf_3(ByRef Value As Variant) As Long: Return IndexOf(Value, 0): End Function
413+
Public Function IndexOf_3(ByRef Value As Variant) As Long: Return IndexOf(Value, pBaseIndex): End Function
366414
[ Hidden ]
367-
Public Sub Sort_2(ByRef Comparer As IComparer): Sort 0, , Comparer: End Sub
415+
Public Sub Sort_2(ByRef Comparer As IComparer): Sort pBaseIndex, , Comparer: End Sub
368416
[ Hidden ]
369-
Public Function BinarySearch_2(ByRef Value As Variant) As Long: Return BinarySearch(0, pIndex, Value): End Function
417+
Public Function BinarySearch_2(ByRef Value As Variant) As Long: Return BinarySearch(pBaseIndex, pIndex, Value): End Function
370418
[ Hidden ]
371-
Public Function BinarySearch_3(ByRef Value As Variant, ByRef Comparer As IComparer) As Long: Return BinarySearch(0, pIndex, Value, Comparer): End Function
419+
Public Function BinarySearch_3(ByRef Value As Variant, ByRef Comparer As IComparer) As Long: Return BinarySearch(pBaseIndex, pIndex, Value, Comparer): End Function
372420
[ Hidden ]
373421
Public Function LastIndexOf_2(ByRef Value As Variant, ByVal Index As Long) As Long: Return LastIndexOf(Value, Index, Index + 1): End Function
374422
[ Hidden ]
375423
Public Function LastIndexOf_3(ByRef Value As Variant, ByVal Index As Long, ByVal GetCount As Long) As Long: Return LastIndexOf(Value, Index, GetCount): End Function
376424
#End Region
377425

378426
#Region "PRIVATE METHODS"
379-
Public Sub New()
380-
ReDim pItems(0 To 1)
381-
End Sub
382-
383427
Private Sub Class_Terminate()
384428
On Error Resume Next
385429
DoEvents

ArrayListLib/Sources/Enumerator.twin

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ Public Class Enumerator
3131
Private Items() As Variant
3232
Private CStep As Long
3333
Private NStep As Long
34+
Private IsEnumerating As Boolean
3435
Private Const E_INVALIDARGS As Long = &H80070057
3536
Private Const S_OK As Long = 0
3637
Private Const S_FALSE As Long = 1
@@ -53,6 +54,8 @@ Public Class Enumerator
5354
PutMem4 ArrPtr(Items) + SAFEARRAY_CELEMENTS_OFFSET, Value
5455
End Property
5556

57+
Public Property Get IsAvailable() As Boolean: IsAvailable = Not IsEnumerating: End Property
58+
5659
Public Sub New()
5760
End Sub
5861

@@ -74,8 +77,13 @@ Public Class Enumerator
7477
End With
7578
VBA.PutMemPtr VarPtrArr(Items), VarPtr(sa)
7679
CStep = GetStep
80+
NStep = 0
7781
MaxValue = GetCount
78-
If CStep < 0 Then CIndex = GetCount - 1
82+
If CStep < 0 Then
83+
CIndex = GetCount - 1
84+
Else
85+
CIndex = 0
86+
End If
7987
End Sub
8088

8189
[ Enumerator ]
@@ -98,6 +106,7 @@ Public Class Enumerator
98106
Err.ReturnHResult = S_OK
99107
Else
100108
If VarPtr(pceltFetched) <> 0 Then pceltFetched = 0
109+
IsEnumerating = False
101110
Err.ReturnHResult = S_FALSE
102111
End If
103112
End If
@@ -114,6 +123,8 @@ Public Class Enumerator
114123

115124
Private Sub Reset() Implements IEnumerator.Reset
116125
CIndex = If(CStep < 0, MaxValue - 1, 0)
126+
NStep = 0
127+
IsEnumerating = True
117128
End Sub
118129

119130
Private Sub Clone(ByRef ppenum As IEnumVARIANT) Implements IEnumerator.Clone

0 commit comments

Comments
 (0)