diff --git a/CodableBloomFilter/Sources/CodableBloomFilter/DeterministicHasher.swift b/CodableBloomFilter/Sources/CodableBloomFilter/DeterministicHasher.swift index 650bc73..acb7748 100644 --- a/CodableBloomFilter/Sources/CodableBloomFilter/DeterministicHasher.swift +++ b/CodableBloomFilter/Sources/CodableBloomFilter/DeterministicHasher.swift @@ -4,33 +4,47 @@ import Foundation public enum DeterministicHasher: String, Codable { case djb2 + case djb2a case sdbm + case fnv1 + case fnv1a } extension DeterministicHasher { func apply(_ hashable: DeterministicallyHashable) -> Int { Array(hashable.hashableData) .map(Int.init) - .reduce(initial, then) + .reduce(offsetBasis, hash) } } // http://www.cse.yorku.ca/~oz/hash.html +// http://www.isthe.com/chongo/tech/comp/fnv/ private extension DeterministicHasher { - var initial: Int { + static let fnvPrime = 16777619 + static let u32mod = 2 << 31 + + var offsetBasis: Int { switch self { - case .djb2: return 5381 + case .djb2, .djb2a: return 5381 case .sdbm: return 0 + case .fnv1, .fnv1a: return 2166136261 } } - func then(result: Int, next: Int) -> Int { + func hash(result: Int, next: Int) -> Int { switch self { case .djb2: return (result << 5) &+ result &+ next + case .djb2a: + return ((result << 5) &+ result ^ next) % Self.u32mod case .sdbm: return next &+ (result << 6) &+ (result << 16) - result + case .fnv1: + return (result * Self.fnvPrime % Self.u32mod) ^ next + case .fnv1a: + return (result ^ next) * Self.fnvPrime % Self.u32mod } } } diff --git a/CodableBloomFilter/Tests/CodableBloomFilterTests/CodableBloomFilterTests.swift b/CodableBloomFilter/Tests/CodableBloomFilterTests/CodableBloomFilterTests.swift index 623fe2d..0d55260 100644 --- a/CodableBloomFilter/Tests/CodableBloomFilterTests/CodableBloomFilterTests.swift +++ b/CodableBloomFilter/Tests/CodableBloomFilterTests/CodableBloomFilterTests.swift @@ -1,9 +1,19 @@ +// Copyright © 2020 Metabolist. All rights reserved. + @testable import CodableBloomFilter import XCTest final class CodableBloomFilterTests: XCTestCase { + func testHashers() { + XCTAssertEqual(DeterministicHasher.djb2.apply("hash"), 6385287881) + XCTAssertEqual(DeterministicHasher.djb2a.apply("hash"), 2087809207) + XCTAssertEqual(DeterministicHasher.sdbm.apply("hash"), 29358318056884782) + XCTAssertEqual(DeterministicHasher.fnv1.apply("hash"), 0xd7918815) + XCTAssertEqual(DeterministicHasher.fnv1a.apply("hash"), 0xcec577d1) + } + func testContains() { - var sut = BloomFilter(hashers: [.djb2, .sdbm], byteCount: 128) + var sut = BloomFilter(hashers: [.djb2, .sdbm, .fnv1, .fnv1a], byteCount: 128) sut.insert("lol") sut.insert("ok")