snippetMinor
How can I improve this 31-bit SuperFastHash implementation in VBA?
Viewed 0 times
thiscanbitsuperfasthashimprovehowimplementationvba
Problem
I'm implementing a variation of the SuperFastHash in VBA for use in Excel (32-bit version, so no LongLong available) to hash strings.
To get around the limitations of signed 32-bit Long values, I'm doing the addition and bit-shifting using Double types, and then converting from Double to Long in a way that truncates it at 31 bits (the maximum positive value -- don't want to deal with two's complement and signs).
I'm getting answers and avoiding overflows so far, but I have a suspicion I'm making some mistakes in translation, since most implementations use all 32 bits of a uint and also deal with individual bytes from an array rather than 16-bit values coming from AscW().
Any suggestions of improvement, especially of the string-handling, bit-shifting, or the final avalanche?
```
Public Function shr(ByVal Value As Long, ByVal Shift As Byte) As Long
shr = Value
If Shift > 0 Then shr = shr \ (2 ^ Shift)
End Function
Public Function shl(ByVal Value As Long, ByVal Shift As Byte) As Long
If Shift > 0 Then
shl = LimitDouble(CDbl(Value) * (2& ^ Shift))
Else
shl = Value
End If
End Function
Public Function LimitDouble(ByVal d As Double) As Long
'' Prevent overflow by lopping off anything beyond 31 bits
Const MaxNumber As Double = 2 ^ 31
LimitDouble = CLng(d - (Fix(d / MaxNumber) * MaxNumber))
End Function
Public Function SuperFastHash(ByVal dataToHash As String) As Long
Dim dataLength As Long
dataLength = Len(dataToHash)
If (dataLength = 0) Then
SuperFastHash = 0
Exit Function
End If
Dim hash As Long
hash = dataLength
Dim remainingBytes As Integer
remainingBytes = dataLength Mod 2
Dim numberOfLoops As Integer
numberOfLoops = dataLength \ 2
Dim currentIndex As Integer
currentIndex = 0
Dim tmp As Double
Do While (numberOfLoops > 0)
hash = LimitDouble(CDbl(hash) + AscW(Mid$(dataToHash, currentIndex + 1, 1)))
tmp = shl(AscW(Mid$(dataToHash
To get around the limitations of signed 32-bit Long values, I'm doing the addition and bit-shifting using Double types, and then converting from Double to Long in a way that truncates it at 31 bits (the maximum positive value -- don't want to deal with two's complement and signs).
I'm getting answers and avoiding overflows so far, but I have a suspicion I'm making some mistakes in translation, since most implementations use all 32 bits of a uint and also deal with individual bytes from an array rather than 16-bit values coming from AscW().
Any suggestions of improvement, especially of the string-handling, bit-shifting, or the final avalanche?
```
Public Function shr(ByVal Value As Long, ByVal Shift As Byte) As Long
shr = Value
If Shift > 0 Then shr = shr \ (2 ^ Shift)
End Function
Public Function shl(ByVal Value As Long, ByVal Shift As Byte) As Long
If Shift > 0 Then
shl = LimitDouble(CDbl(Value) * (2& ^ Shift))
Else
shl = Value
End If
End Function
Public Function LimitDouble(ByVal d As Double) As Long
'' Prevent overflow by lopping off anything beyond 31 bits
Const MaxNumber As Double = 2 ^ 31
LimitDouble = CLng(d - (Fix(d / MaxNumber) * MaxNumber))
End Function
Public Function SuperFastHash(ByVal dataToHash As String) As Long
Dim dataLength As Long
dataLength = Len(dataToHash)
If (dataLength = 0) Then
SuperFastHash = 0
Exit Function
End If
Dim hash As Long
hash = dataLength
Dim remainingBytes As Integer
remainingBytes = dataLength Mod 2
Dim numberOfLoops As Integer
numberOfLoops = dataLength \ 2
Dim currentIndex As Integer
currentIndex = 0
Dim tmp As Double
Do While (numberOfLoops > 0)
hash = LimitDouble(CDbl(hash) + AscW(Mid$(dataToHash, currentIndex + 1, 1)))
tmp = shl(AscW(Mid$(dataToHash
Solution
This is quite possibly the most clever VBA code I have ever seen.
There's not much room for improvement here, except a couple naming nitpicks:
As for string handling, as per this source
AscW(Mid$(..)) considerations
Don't copy too many characters with Mid$. AscW only examines the first
character anyway. AscW(Mid$(s, x, 1)) is the best call. Note that if
you call AscW(Mid$(s, x)) without the third parameter, Mid$ executes
slowly when s is a long string.
Example of potentially slow code:
The above is better written as:
There's not much room for improvement here, except a couple naming nitpicks:
- If that's what they stand for, functions
shrandshlcould afford to be calledShiftRightandShiftLeft.
- I don't see a reason for
shrandshlto not follow the PascalCasing naming convention.
- Given the almost-flawlessly consistent camelCasing convention for locals & parameters, I don't see a reason for
ValueandShiftparameters to not stick to it. (except perhaps VBA/VB6's stubornness with casing?)
As for string handling, as per this source
AscW is faster than Asc so again, good call:AscW(Mid$(..)) considerations
Don't copy too many characters with Mid$. AscW only examines the first
character anyway. AscW(Mid$(s, x, 1)) is the best call. Note that if
you call AscW(Mid$(s, x)) without the third parameter, Mid$ executes
slowly when s is a long string.
Example of potentially slow code:
For x = 1 To Len(s)
If AscW(Mid$(s, x)) = ... Then ...
NextThe above is better written as:
For x = 1 To Len(s)
If AscW(Mid$(s, x, 1)) = ... Then ...
NextCode Snippets
For x = 1 To Len(s)
If AscW(Mid$(s, x)) = ... Then ...
NextFor x = 1 To Len(s)
If AscW(Mid$(s, x, 1)) = ... Then ...
NextContext
StackExchange Code Review Q#16377, answer score: 7
Revisions (0)
No revisions yet.