VB6 how to get C-like integer overflow

2019-06-05 04:40发布

问题:

I want to port this simple hash algorithm to VB6.

I have come up with:

Public Function simpleHash(hashString As String) As Long
Dim hash As Long
Dim c As Long
Dim i As Integer

    On Local Error Resume Next

    hash = 5381
    For i = 1 To Len(hashString)
        c = AscW(Mid$(hashString, i, 1))
        hash = hash * 33 + c
    Next i

    simpleHash = hash
End Function

The problem is, despite my On Error statement which suppresses the Error 6: Overflow exceptions, the variable hash is not updated any more if an overflow occurs.

How can I fix that or implement this algorithm differently?

回答1:

I would question whether it makes sense to hash UTF-16LE ("Unicode") this way. It might make a lot more sense to convert your VB String to UTF-8 and then hash that.

While I can't find any test vectors for djb2 to validate my own implementation it seems to run quite quickly:

Private Type CURRENCY_CURRENCY
    Value As Currency
End Type

Private Type CURRENCY_2LONGS
    ValueLo As Long
    ValueHi As Long
End Type

Public Function djb2(ByVal StringToHash As String) As Long
    Dim C2L As CURRENCY_2LONGS
    Dim CC As CURRENCY_CURRENCY
    Dim I As Long

    C2L.ValueLo = 5381
    For I = 1 To Len(StringToHash)
        LSet CC = C2L
        CC.Value = CC.Value * 33@ + CCur(AscW(Mid$(StringToHash, I, 1))) / 10000@
        LSet C2L = CC
        C2L.ValueHi = 0
    Next I

    djb2 = C2L.ValueLo
End Function


回答2:

I found a workaround using Currency and doing the overflow math myself (and it turns out that VB6 Mod operator also won't work on Currency):

Const pow2_32 As Currency = 2@ ^ 32
Const signed32Max As Currency = 2@ ^ 31 - 1

Public Function simpleHash(hashString As String) As Long
Dim hash As Currency
Dim i As Long

    hash = 5381
    For i = 1 To Len(hashString)
        hash = hash * 33@ + CCur(AscW(Mid$(hashString, i, 1)))
        While hash >= pow2_32
            hash = hash - pow2_32
        Wend
    Next i

    If hash > signed32Max Then
        hash = hash - pow2_32
    End If

    simpleHash = CLng(hash)
End Function

However, see Bob77's answer for a solution that is almost twice as fast as mine.