18

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.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Rasto
  • 17,204
  • 47
  • 154
  • 245
  • 2
    if it can be simple, why not use an [MD5 hash](http://stackoverflow.com/a/2018626/1219956) edit: [swift version](http://stackoverflow.com/questions/24123518/how-to-use-cc-md5-method-in-swift-language) – Fonix Mar 09 '16 at 03:06
  • @Fonix Yes, that could be solution. There are many answer to the question you are linking - could you recommend any of those with stable and fast MD5 hash implementation in Swift? – Rasto Mar 09 '16 at 03:13
  • see the second link in my first comment, but otherwise there are tons of solutions with a quick google – Fonix Mar 09 '16 at 03:16

1 Answers1

26

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
Warren Stringer
  • 1,692
  • 1
  • 17
  • 14
  • 1
    Slightly more condensed version of Warren's first answer as a computed property on a String extension: `[UInt8](utf8).reduce(UInt64(5381)) { 127 * ($0 & 0x00ffffffffffffff) + UInt64($1) }` – Jacob Bartlett Feb 08 '22 at 11:20
  • Is there any way to reverse this operation? To get `String` value from the hash – Adam Feb 16 '22 at 10:16
  • 1
    @Adam Nope. Most hashes lose information. The main utility of a hash is to shorten a long value into a shorter value that doesn't collide with other shortened values. – Warren Stringer Feb 17 '22 at 16:50