Ok, the results are in. The following is about as fast as pure VB6 code is going to get a string array sorted.
I had already worked out swapping of BSTR pointers so the BSTR strings didn't need to be moved or copied to get this done. However, thanks go out to VanGoghGaming for suggesting aliasing all these BSTR pointers with a Long array so that we don't need lots of GetMem4 calls. It turns out that just doing Long variable assignments is quite a bit faster than calls to GetMem4.
There's another thread found here that compares the speeds of several methods of sorting string arrays, and the following is the clear winner.
As a note, if you need to do something like sort a UDT array based on some string item, the following code isn't what you want. This code is highly optimized for specifically sorting a single dimension string array.
This method is recursive, but it's still fairly memory efficient, as the BSTR strings are never copied.
I haven't made an attachment, as it's easy enough to copy-paste. Ideally, you'd want to place both procedures (the Public one and the Private one) in a BAS module somewhere.
If anyone thinks they can do this faster, bounce over to this thread and give it a go. I'll work your method into the timings with the other attempts.
Update on June 6, 2024: It worked absolutely fine as originally posted, but I cleaned up the way I do the string array aliasing so that a second copy of the SafeArray structure isn't needed.
I had already worked out swapping of BSTR pointers so the BSTR strings didn't need to be moved or copied to get this done. However, thanks go out to VanGoghGaming for suggesting aliasing all these BSTR pointers with a Long array so that we don't need lots of GetMem4 calls. It turns out that just doing Long variable assignments is quite a bit faster than calls to GetMem4.
There's another thread found here that compares the speeds of several methods of sorting string arrays, and the following is the clear winner.
As a note, if you need to do something like sort a UDT array based on some string item, the following code isn't what you want. This code is highly optimized for specifically sorting a single dimension string array.
This method is recursive, but it's still fairly memory efficient, as the BSTR strings are never copied.
I haven't made an attachment, as it's easy enough to copy-paste. Ideally, you'd want to place both procedures (the Public one and the Private one) in a BAS module somewhere.
Code:
Option Explicit
'
Private Declare Sub GetMem4 Lib "msvbvm60.dll" (ByRef Source As Any, ByRef Dest As Any)
Private Declare Sub GetMem2 Lib "msvbvm60.dll" (ByRef Source As Any, ByRef Dest As Any)
Private Declare Function ArrPtr Lib "msvbvm60.dll" Alias "VarPtr" (a() As Any) As Long
Private Declare Function StrArrPtr Lib "msvbvm60.dll" Alias "__vbaRefVarAry" (StringArray As Variant) As Long ' Just pass string array as argument (NOT variant).
'
Public Sub RecursiveSortStringsEx(ByRef ToBeSorted() As String)
' A recursive quicksort. But nowhere are strings actually copied or moved, so it's memory efficient and fast.
' This just gets things going. A call to LocalRecursiveSortStrings does the actual sorting.
' The array is completely validated, and this procedure returns if it's not a valid one-dimension string array with data.
'
' Get ToBeSorted() string array's SafeArray pointer, and make sure we've got an array.
' Note that 0 to -1 array is checked down in LocalRecursiveSortStrings call.
Dim ppSaToSort As Long ' Pointer to pointer to ToBeSorted's SafeArray structure.
Dim pSaToSort As Long ' Pointer to ToBeSorted's SafeArray structure.
ppSaToSort = StrArrPtr(ToBeSorted) ' We must use this alternate API call so it doesn't try to do Unicode to ANSI conversion.
GetMem4 ByVal ppSaToSort, pSaToSort ' Dereference pointer so we can check certain things in the SafeArray.
If pSaToSort = 0& Then Exit Sub ' No SafeArray found.
Dim cDims As Integer ' Check to make sure we just have one dimension.
GetMem2 ByVal pSaToSort, cDims ' Get number of dimensions in string array.
If cDims <> 1 Then Exit Sub ' Not a ONE dimensional array.
'
' Get pointer to temp Long array that we'll be using to alias our string array BSTR pointers.
Dim lAlias() As Long ' Uninitialized array of longs we'll use to alias the string array SafeArray.
Dim ppSaAlias As Long ' Pointer to pSaAlias's SafeArray.
ppSaAlias = ArrPtr(lAlias) ' Get pointer to pointer to SafeArray, but there's initially no SafeArray, so this points to zero.
'
' And now, let's do the actual alias of the Long array's SafeArray onto the String array's SafeArray.
GetMem4 ByVal ppSaToSort, ByVal ppSaAlias ' We're using the same SafeArray, with two array variables pointing at it.
'
' We're now ready to sort, using our lAlias array for swapping.
LocalRecursiveSortStrings ToBeSorted, LBound(ToBeSorted), UBound(ToBeSorted), lAlias
'
' And restore our lAlias's SafeArray pointer so memory isn't corrupted.
GetMem4 0&, ByVal ppSaAlias ' Effectively setting lAlias back to an undimensioned state.
End Sub
Private Sub LocalRecursiveSortStrings(ByRef ToBeSorted() As String, ByVal TheLeft As Long, ByVal TheRigt As Long, ByRef lAlias() As Long)
' This is a recursive quicksort (but must be called from RecursiveSortStringsEx).
'
Static i As Long, j As Long, t As Long ' Static so we don't pile up local recursive variables.
Static sPivot As String, pPivot As Long ' Static so we don't pile up local recursive variables.
If TheLeft < TheRigt Then
GetMem4 ByVal VarPtr(sPivot), pPivot ' Save original string pointer.
GetMem4 ByVal VarPtr(ToBeSorted(TheRigt)), ByVal VarPtr(sPivot) ' Alias our pivot string, so we don't have to create another BSTR.
i = TheLeft - 1&
For j = TheLeft To TheRigt - 1&
If ToBeSorted(j) <= sPivot Then
i = i + 1&
t = lAlias(i): lAlias(i) = lAlias(j): lAlias(j) = t ' Swap i & j.
End If
Next
GetMem4 pPivot, ByVal VarPtr(sPivot) ' Cleanup pivot string aliasing.
i = i + 1& ' Set pivot.
t = lAlias(i): lAlias(i) = lAlias(TheRigt): lAlias(TheRigt) = t ' Swap i & TheRigt.
LocalRecursiveSortStrings ToBeSorted, TheLeft, i - 1&, lAlias ' Sort each side of pivot, recursively.
LocalRecursiveSortStrings ToBeSorted, i + 1&, TheRigt, lAlias ' Sort each side of pivot, recursively.
End If
End Sub
Update on June 6, 2024: It worked absolutely fine as originally posted, but I cleaned up the way I do the string array aliasing so that a second copy of the SafeArray structure isn't needed.