在swift中实现FNV哈希

时间:2014-06-10 05:38:07

标签: algorithm hash swift

我正在尝试在swift中实现一个FNV哈希版本。这是在Objective-C中:

+ (uint32_t)hash:(uint8_t *)a length:(uint32_t)length
{
    uint8_t *p;
    uint32_t x;

    p = a;
    x = *p << 7;
    for (int i=0; i<length; i++) {
        x = (1000003 * x) ^ *p++;
        x ^= length;
    }
    if (x == -1) {
        x = -2;
    }
    return x;
}

这是我尝试将其移植到swift:

func hashFNV(data: UInt8[]) -> UInt32 {
    var x = data[0] << 7

    for byte in data {
        x *= 1000003
        x ^= byte
        x ^= data.count
    }
    if x == -1 {
        x = -2
    }
    return x
}

它编译但在运行时导致错误:

EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP,subcode=0x0)

我在操场上尝试时出现同样的错误:

Playground execution failed: error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0).
The process has been left at the point where it was interrupted, use "thread return -x" to return to the state before expression evaluation.
* thread #1: tid = 0x619fa, 0x000000010d119aad, queue = 'com.apple.main-thread', stop reason = EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
  * frame #0: 0x000000010d119aad
    frame #1: 0x0000000100204880 libswift_stdlib_core.dylib`value witness table for Swift.Int + 160

我认为这可能与溢出有关,但以下代码也会因同样的错误而失败:

func hashFNV(data: UInt8[]) -> UInt32 {
    var x = UInt32(data[0]) << 7

    for byte in data {
        x = 1000003 &* x
        x ^= byte
        x ^= data.count
    }
    if x == -1 {
        x = -2
    }
    return x
}

编辑:

实际上,我不应该尝试将-2分配给x会导致编译错误吗?我认为swift不会隐含地从看起来像Int(-2)到UInt32(x)。

x ^= byte行相同。 byte应为UInt8,x为UInt32。

编辑2:

这是编译错误(请参阅下面的评论)。

修复了编译错误,在运行时仍然失败:

func hashFNV(data: UInt8[]) -> UInt32 {
    var x = Int(data[0]) << 7

    for byte in data {
        x = 1000003 &* x
        x ^= Int(byte)
        x ^= data.count
    }
    if x == -1 {
        x = -2
    }
    return UInt32(x)
}

2 个答案:

答案 0 :(得分:0)

如果您仍在寻找实现,这是我的。它的构建很像标准库中的常规默认Hasher

struct HasherFNV1a {

    private var hash: UInt = 14_695_981_039_346_656_037
    private let prime: UInt = 1_099_511_628_211

    mutating func combine<S: Sequence>(_ sequence: S) where S.Element == UInt8 {
        for byte in sequence {
            hash ^= UInt(byte)
            hash = hash &* prime
        }
    }

    func finalize() -> Int {
        Int(truncatingIfNeeded: hash)
    }
}

extension HasherFNV1a {

    mutating func combine(_ string: String) {
        combine(string.utf8)
    }

    mutating func combine(_ bool: Bool) {
        combine(CollectionOfOne(bool ? 1 : 0))
    }
}

请记住,这是FNV1a,如果您确实需要FNV1,则只需在循环中切换两行即可。

答案 1 :(得分:-1)

我找到this GPL Swift implementation

//
// FNVHash.swift
//
// A Swift implementation of the Fowler–Noll–Vo (FNV) hash function
// See http://www.isthe.com/chongo/tech/comp/fnv/
//
// Created by Mauricio Santos on 3/9/15.
import Foundation
// MARK:- Constants
private struct Constants {
// FNV parameters
#if arch(arm64) || arch(x86_64) // 64-bit
static let OffsetBasis: UInt = 14695981039346656037
static let FNVPrime: UInt = 1099511628211
#else // 32-bit
static let OffsetBasis: UInt = 2166136261
static let FNVPrime: UInt = 16777619
#endif
}
// MARK:- Public API
/// Calculates FNV-1 hash from a raw byte array.
public func fnv1(bytes: [UInt8]) -> UInt {
var hash = Constants.OffsetBasis
for byte in bytes {
hash = hash &* Constants.FNVPrime // &* means multiply with overflow
hash ^= UInt(byte)
}
return hash
}
/// Calculates FNV-1a hash from a raw byte array.
public func fnv1a(bytes: [UInt8]) -> UInt {
var hash = Constants.OffsetBasis
for byte in bytes {
hash ^= UInt(byte)
hash = hash &* Constants.FNVPrime
}
return hash
}
/// Calculates FNV-1 hash from a String using it's UTF8 representation.
public func fnv1(str: String) -> UInt {
return fnv1(bytesFromString(str))
}
/// Calculates FNV-1a hash from a String using it's UTF8 representation.
public func fnv1a(str: String) -> UInt {
return fnv1a(bytesFromString(str))
}
/// Calculates FNV-1 hash from an integer type.
public func fnv1<T: IntegerType>(value: T) -> UInt {
return fnv1(bytesFromNumber(value))
}
/// Calculates FNV-1a hash from an integer type.
public func fnv1a<T: IntegerType>(value: T) -> UInt {
return fnv1a(bytesFromNumber(value))
}
/// Calculates FNV-1 hash from a floating point type.
public func fnv1<T: FloatingPointType>(value: T) -> UInt {
return fnv1(bytesFromNumber(value))
}
/// Calculates FNV-1a hash from a floating point type.
public func fnv1a<T: FloatingPointType>(value: T) -> UInt {
return fnv1a(bytesFromNumber(value))
}
// MARK:- Private helper functions
private func bytesFromString(str: String) -> [UInt8] {
var byteArray = [UInt8]()
for codeUnit in str.utf8 {
byteArray.append(codeUnit)
}
return byteArray
}
private func bytesFromNumber<T>(var value: T) -> [UInt8] {
return withUnsafePointer(&value) {
Array(UnsafeBufferPointer(start: UnsafePointer<UInt8>($0), count: sizeof(T)))
}
}