Hash value of String that would be stable across i

2020-03-10 02:15发布

问题:

In documentation String.hash for iOS it says:

You should not rely on this property having the same hash value across releases of OS X.

(strange why they speak of OS X in iOS documentation)

Well, I need a hasshing function that will not change with iOS releases. It can be simple I do not need anything like SHA. Is there some library for that?

There is another question about this here but the accepted (and only) answer there simply states that we should respect the note in documentation.

回答1:

Here is a non-crypto hash, for Swift 3:

 func strHash(_ str: String) -> UInt64 {
    var result = UInt64 (5381)
    let buf = [UInt8](str.utf8)
    for b in buf {
        result = 127 * (result & 0x00ffffffffffffff) + UInt64(b)
    }
    return result
 }

It was derived somewhat from a C++11 constexpr

    constexpr uint64_t str2int(char const *input) {
    return *input                      // test for null terminator
    ? (static_cast<uint64_t>(*input) + // add char to end
       127 * ((str2int(input + 1)      // prime 127 shifts left almost 7 bits
               & 0x00ffffffffffffff))) // mask right 56 bits
    : 5381;                            // start with prime number 5381
}

Unfortunately, the two don't yield the same hash. To do that you'd need to reverse the iterator order in strHash:

for b in buf.reversed() {...}

But that will run 13x slower, somewhat comparable to the djb2hash String extension that I got from https://useyourloaf.com/blog/swift-hashable/

Here are some benchmarks, for a million iterations:

hashValue execution time: 0.147760987281799
strHash execution time:   1.45974600315094
strHashReversed time:    18.7755110263824
djb2hash execution time: 16.0091370344162
sdbmhash crashed

For C++, the str2Int is roughly as fast as Swift 3's hashValue:

str2int execution time: 0.136421