You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Three generic array classes cover the full range of twinBASIC value and reference types. All three implement IArray and share an identical public API; the differences are limited to assignment semantics, equality testing, and a handful of restrictions in UDTArray.
Class
Element type
Assignment
Equality
For Each
ValueArray(Of T)
Scalar value types (Long, String, Boolean, …)
Property Let / =
= operator
Yes
ObjectArray(Of T)
Reference / COM types
Property Set / Set
Is (pointer identity)
Yes
UDTArray(Of T)
UDT structs exposing Compare(other As T) As Long
Property Let / =
T.Compare(other) = 0
No, raises error 5
Note
Some UDT structs may not have meaningful comparison operation. Compare should then return -2, or any value other than -1, 0 or +1.
Construction
DimaAsNewValueArray(OfLong)DimbAsNewObjectArray(OfCollection)DimcAsNewUDTArray(OfMyStruct)' MyStruct must expose Compare(other As MyStruct) As Long
No arguments are required. Each array starts empty with no memory allocated. LBound reports 1 until set otherwise.
Bounds
The lower bound is configurable: it defaults to 1 (matching Option Base 1) but can be set to any integer — negative, zero, or positive. UBound is always derived as LBound + Count - 1. All index-taking members use the current LBound-relative coordinate space.
Member
Description
LBound&(Get/Let)
Lower bound of the array. Defaults to 1. Can be set to any integer. If the array is already initialized or lb ≠ 1, calls Reserve 1 to ensure the backing array exists first. Setting LBound = 1 on an uninitialized array is a no-op.
UBound&(Get)
Upper bound: always LBound + Count - 1. Returns LBound - 1 for an empty array.
SetDim(from&, to&)
Set LBound and Count in one call. Equivalent to Resize(to - from + 1) followed by LBound = from. New slots are initialised to default values. Expects to ≥ from - 1.
Indexing
Assignment uses Property Let in ValueArray and UDTArray, and Property Set in ObjectArray.
Member
Description
Item(index&) As T(default)
Get or set the element at index. Expects LBound ≤ index ≤ UBound.
First As T
Get or set the first element (Item(LBound)). Expects non-empty.
Last As T
Get or set the last element (Item(UBound)). Expects non-empty.
State
Member
Description
Count As Long
Number of elements currently stored.
Capacity As Long
Number of slots currently allocated. Returns 0 if uninitialized.
IsEmpty As Boolean
True when Count = 0.
Adding elements
Member
Description
Append(item As T)
Append a single item.
Append(items() As T)
Append all elements of a native array. No-op if null or empty.
AppendIA(items As IArray)
Append all elements of an IArray. No-op if Nothing or empty. In UDTArray, items must be Nothing or a UDTArray(Of T); any other type raises error 5.
Prepend(item As T)
Insert a single item at LBound.
Prepend(items() As T)
Insert all elements of a native array at the front. No-op if null or empty.
PrependIA(items As IArray)
Insert all elements of an IArray at the front. No-op if Nothing or empty. In UDTArray, items must be Nothing or a UDTArray(Of T); any other type raises error 5.
InsertAt(index As Long, value As T)
Insert value before index (valid range LBound to UBound + 1).
InsertArrayAt(index As Long, items() As T)
Insert all elements of a native array at index. No-op if null or empty. Valid range for index: LBound to UBound + 1.
InsertIAAt(index As Long, items As IArray)
Insert all elements of an IArray at index. No-op if Nothing or empty. In UDTArray, items must be Nothing or a UDTArray(Of T); any other type raises error 5. Valid range for index: LBound to UBound + 1.
Removing elements
Member
Description
RemoveAt(index&)
Remove the element at index. Shifts subsequent elements left, then clears the freed slot via Wipe. Expects LBound ≤ index ≤ UBound.
RemoveFirst()
Remove the first element. Expects non-empty.
RemoveLast()
Remove the last element. Expects non-empty.
SwapRemove(index&)
O(1) unordered removal: overwrites mData(index) with mData(UBound), then removes the last element by decrementing Count and wiping the freed slot. Does not preserve element order. Expects LBound ≤ index ≤ UBound.
RemoveRange(from&, count&)
Remove count contiguous elements starting at from. Shifts the tail left, then wipes the freed tail slots via Wipe. Expects count ≥ 1 and from..from+count-1 within LBound..UBound.
RemoveOne(value As T) As Boolean
Remove the first occurrence of value. Returns True if removed. Clears the vacated tail slots via Wipe.
RemoveAll(value As T) As Long
Remove all occurrences of value by compacting in place. Returns the count removed. Clears the vacated tail slots via Wipe.
Deduplicate() As Long
Remove consecutive duplicate elements in a single left-to-right pass. Each run of equal adjacent values is collapsed to its first element. Returns the number of elements removed. Clears the vacated tail slots via Wipe. Non-adjacent duplicates are unaffected; call Sort first if all duplicates should be removed.
Taking elements
These methods remove and return an element in a single call.
Member
Description
TakeAt(index&) As T
Remove and return the element at index. Expects LBound ≤ index ≤ UBound.
TakeFirst() As T
Remove and return the first element. Expects non-empty.
TakeLast() As T
Remove and return the last element. Expects non-empty.
Searching
Equality is type-specific: ValueArray uses =, ObjectArray uses Is (pointer identity), UDTArray uses T.Compare(other) = 0.
Member
Description
IndexOf(value As T, from& = InvalidIndex) As Long
First index of value at or after from. When from = InvalidIndex (the default), searches from LBound. Returns InvalidIndex if not found. Expects from ≥ LBound.
LastIndexOf(value As T, from& = InvalidIndex) As Long
Last index of value at or before from. When from = InvalidIndex (the default), searches from UBound. Returns InvalidIndex if not found. Expects from ≤ UBound.
Contains(value As T) As Boolean
True if value appears at least once.
CountOf(value As T) As Long
Number of occurrences of value.
IsDefaultAt(index&) As Boolean
True if the slot at index holds the default value for T: = default in ValueArray, Is Nothing in ObjectArray, T.Compare(default) = 0 in UDTArray. index is in backing-storage space and may address slots beyond UBound (up to Capacity - 1). Expects LBound ≤ index ≤ LBound+Capacity-1.
StartsWith(value As T) As Boolean
True if Count > 0 and the first element equals value.
EndsWith(value As T) As Boolean
True if Count > 0 and the last element equals value.
Bulk operations
Member
Description
Fill(value As T)
Set every element to value. No-op on an empty array.
MoveItem(from&, to&)
Relocate the element at from to position to, shifting elements between them. No-op when from = to. Expects both indices to be LBound ≤ index ≤ UBound.
Swap(a&, b&)
Exchange elements at positions a and b. No-op when a = b. Expects both indices to be LBound ≤ index ≤ UBound.
Rotate(n&)
Right-rotate all elements by n positions: element at UBound - n + 1 moves to LBound, etc. Negative n rotates left. n is normalized modulo Count before use. No-op on an empty array or when n mod Count = 0. Implemented via three-reversal algorithm.
Reverse(from&, to&)
Reverse the sub-range [from, to] in-place by swapping elements from both ends toward the middle. No-op when from ≥ to. Expects indices to be within LBound..UBound.
Move(first&, last&, by&)
Shift the contiguous block [first..last] by by positions using RtlMoveMemory. Positive by shifts right; negative by shifts left. The vacated source slots are zeroed via RtlZeroMemory. Expects first ≥ LBound, last ≤ LBound + Capacity - 1, and that the destination range fits within allocated capacity.
Type Information
Member
Description
Compare(index1&, index2&) As Long
Compare the elements at index1 and index2. Returns a negative value if index1 < index2, 0 if equal, positive if index1 > index2. Comparison semantics are type-specific: =/</> in ValueArray; ObjPtr in ObjectArray; T.Compare(other) in UDTArray. Expects both indices to be within LBound..UBound.
Comparator() As Comparator
Returns a type-specific function delegate (Delegate Function Comparator(first As Any, second As Any, Optional context As Any) As Long) used internally by pointer-based sort routines.
ElementSize() As Long
Returns the byte size of one element (LenB(Of T) in ValueArray and UDTArray; pointer size in ObjectArray).
Sort elements in ascending order (natural order for the array's type). When lo/hi are InvalidIndex (the default), sorts all elements. Uses introsort via the MemOps type and the array's Comparator delegate: median-of-three Lomuto quicksort with a heapsort fallback when recursion depth exceeds 2 * ⌊log₂(n)⌋ (computed automatically when depthLimit = -1), and insertion sort for subarrays of 16 elements or fewer. In-place, O(n log n) worst case.
HeapSort(lo& = InvalidIndex, hi& = InvalidIndex)
Heap-sort elements in the range [lo, hi]. When lo/hi are InvalidIndex, sorts all elements. O(n log n) worst case.
Insertion-sort elements in the range [lo, hi]. When lo/hi are InvalidIndex, sorts all elements. O(n²) worst case but efficient for small or nearly-sorted data.
Slicing and copying
Member
Description
Mid(pos&, len& = -1, lBound& = 1) As IArray
Return a new array of the same type containing len elements starting at pos. len = -1 means "to the end". The returned slice has its LBound set to lBound (defaults to 1). In UDTArray, the source must be a UDTArray(Of T) (always satisfied when called as Me.Mid(...)); passing a foreign IArray to the private constructor produces an empty result.
View(pos&, len& = -1, viewLBound& = 1) As T()
Return a zero-copy T() array that points directly into internal storage, covering len elements starting at pos. len = -1 means "to the end". The view array's lower bound is viewLBound (defaults to 1). Returns an empty array when len = 0. The backing data must not be reallocated while the view is in use.
View(viewOut As T(), pos&, len& = -1, viewLBound& = 1)
Fill an existing array variable viewOut with the same zero-copy view. Implements IArray.View.
Clone() As IArray
Return a full copy of the array as a new instance of the same type, preserving LBound.
Memory management
Member
Description
Reserve(capacity&)
Ensure at least capacity slots are allocated without changing Count.
Resize(count&)
Set Count to count. When shrinking, slots past the new UBound are cleared via Wipe; existing elements within the new range are preserved. LBound is unchanged.
ResizeToAtLeast(count&)
Call Resize(count) only if count > Count.
Squeeze()
Release excess capacity so Capacity = Count, using ReDim Preserve. Deallocates entirely when empty.
Clear()
Remove all elements and deallocate memory (Erase mData). Resets LBound to 1 on next access.
Wipe(from&, to&)
Overwrite slots from..to with the default value of T: zero / empty string in ValueArray and UDTArray, Nothing in ObjectArray. Expects the range to be within allocated capacity.
Iteration
ValueArray and ObjectArray implement IEnumVARIANT via ArrayIterator, enabling For Each:
UDTArray cannot be enumerated with For Each: _NewEnum raises error 5 because TwinBasic cannot box UDT values into Variant for the COM enumeration protocol.
Variant interop
Available in ValueArray and ObjectArray only. Both methods raise error 5 in UDTArray.
Member
Description
GetVarItem(index&, result As Variant)
Copy the element at index into result as a Variant. Expects LBound ≤ index ≤ UBound.
SetVarItem(index&, value&)
Set the element at index from value. Expects LBound ≤ index ≤ UBound.
Pointer access
Member
Description
Ptr() As LongPtr
Pointer to the first element in the backing array. Returns 0 when no memory is allocated. An empty-but-allocated array may return a non-null pointer.
Memory growth policy
Grow(by) doubles the current capacity when more space is needed, targeting Max(MinSize, cap * 2, cap + by) slots. The allocation always preserves the current LBound via ReDim Preserve mData(lb To lb + newCap - 1). This keeps amortised Append cost O(1).
Preconditions
All public methods assert their preconditions with Debug.Assert. These checks fire only in debug builds and are stripped in release builds.