Skip to content

Commit

Permalink
Merge branch 'main'
Browse files Browse the repository at this point in the history
  • Loading branch information
crichez committed Feb 4, 2022
2 parents d943285 + e7d8156 commit 2ec3c78
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 91 deletions.
13 changes: 11 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ To depend on it, include it in your `Package.swift` dependencies:

```swift
.package(
name: "FowlerNollVo:,
name: "FowlerNollVo",
url: "https://github.com/crichez/swift-fowler-noll-vo",
.upToNextMinor(from: "0.1.0")),
.upToNextMinor(from: "0.2.0")),
```

**All versions below 1.0.0 are considered pre-release.**
Expand Down Expand Up @@ -67,6 +67,15 @@ The following hasher types are built-in:
* `FNV512` & `FNV512a`
* `FNV1024` & `FNV1024a`

**Note:** `FNV128` and hashers with larger digests all use `DoubleWidth`
from [the swift numerics project](https://github.com/apple/swift-numerics).
That code was included in this project under its original Apache 2.0 license,
and is still considered experimental.

Although performance from 32 to 64-bit digests is similar,
performance quickly degrades with larger digests. Getting a 1024-bit digest
from 64 bits of input data takes around 0.33 seconds on a M1 MacBook Pro.

### Usage

```swift
Expand Down
10 changes: 2 additions & 8 deletions Sources/FowlerNollVo/FNVHashable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,8 @@ extension Float: FNVHashable {
}
}

#if arch(arm)
@available(macOS 11, *)
@available(macCatalyst 14, *)
@available(macOSApplicationExtension 11, *)
@available(macCatalystApplicationExtension 14, *)
@available(iOS 14, *)
@available(watchOS 7, *)
@available(tvOS 14, *)
#if swift(>=5.4) && !((os(macOS) || targetEnvironment(macCatalyst)) && arch(x86_64))
@available(macOS 11, iOS 14, watchOS 7, tvOS 14, *)
extension Float16: FNVHashable {
public func hash<Hasher>(into hasher: inout Hasher) where Hasher : FNVHasher {
hasher.combine(isZero ? Float16.zero.bitPattern : bitPattern)
Expand Down
81 changes: 44 additions & 37 deletions Sources/FowlerNollVo/Hashers/FNV1024.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,62 +5,69 @@
// Created by Christopher Richez on 2/2/22.
//

extension DoubleWidth where Base == DoubleWidth<DoubleWidth<DoubleWidth<UInt64>>> {
fileprivate static var fnvPrime: DoubleWidth<DoubleWidth<DoubleWidth<DoubleWidth<UInt64>>>> {
DoubleWidth<DoubleWidth<DoubleWidth<DoubleWidth<UInt64>>>>(
DoubleWidth<DoubleWidth<DoubleWidth<UInt64>>>(
DoubleWidth<DoubleWidth<UInt64>>(
DoubleWidth<UInt64>(0x0000000000000000, 0x0000000000000000),
DoubleWidth<UInt64>(0x0000000000000000, 0x0000000000000000)
/// A 1024-bit digest that behaves like an unsigned integer.
///
/// `DoubleWidth` is from the [Swift numerics](https://github.com/apple/swift-numerics)
/// open-source project. Expect all operations on a `Digest1024` value to perform
/// significantly worse than operations on `UInt64` and other standard library native integers.
public typealias Digest1024 = DoubleWidth<Digest512>

extension Digest1024 {
fileprivate static var fnvPrime: Digest1024 {
Digest1024(
Digest512(
Digest256(
Digest128(0x0000000000000000, 0x0000000000000000),
Digest128(0x0000000000000000, 0x0000000000000000)
),
DoubleWidth<DoubleWidth<UInt64>>(
DoubleWidth<UInt64>(0x0000000000000000, 0x0000010000000000),
DoubleWidth<UInt64>(0x0000000000000000, 0x0000000000000000)
Digest256(
Digest128(0x0000000000000000, 0x0000010000000000),
Digest128(0x0000000000000000, 0x0000000000000000)
)
),
DoubleWidth<DoubleWidth<DoubleWidth<UInt64>>>(
DoubleWidth<DoubleWidth<UInt64>>(
DoubleWidth<UInt64>(0x0000000000000000, 0x0000000000000000),
DoubleWidth<UInt64>(0x0000000000000000, 0x0000000000000000)
Digest512(
Digest256(
Digest128(0x0000000000000000, 0x0000000000000000),
Digest128(0x0000000000000000, 0x0000000000000000)
),
DoubleWidth<DoubleWidth<UInt64>>(
DoubleWidth<UInt64>(0x0000000000000000, 0x0000000000000000),
DoubleWidth<UInt64>(0x0000000000000000, 0x000000000000018D)
Digest256(
Digest128(0x0000000000000000, 0x0000000000000000),
Digest128(0x0000000000000000, 0x000000000000018D)
)
)
)
}

fileprivate static var fnvOffset: DoubleWidth<DoubleWidth<DoubleWidth<DoubleWidth<UInt64>>>> {
DoubleWidth<DoubleWidth<DoubleWidth<DoubleWidth<UInt64>>>>(
DoubleWidth<DoubleWidth<DoubleWidth<UInt64>>>(
DoubleWidth<DoubleWidth<UInt64>>(
DoubleWidth<UInt64>(0x0000000000000000, 0x005f7a76758ecc4d),
DoubleWidth<UInt64>(0x32e56d5a591028b7, 0x4b29fc4223fdada1)
fileprivate static var fnvOffset: Digest1024 {
Digest1024(
Digest512(
Digest256(
Digest128(0x0000000000000000, 0x005f7a76758ecc4d),
Digest128(0x32e56d5a591028b7, 0x4b29fc4223fdada1)
),
DoubleWidth<DoubleWidth<UInt64>>(
DoubleWidth<UInt64>(0x6c3bf34eda3674da, 0x9a21d90000000000),
DoubleWidth<UInt64>(0x0000000000000000, 0x0000000000000000)
Digest256(
Digest128(0x6c3bf34eda3674da, 0x9a21d90000000000),
Digest128(0x0000000000000000, 0x0000000000000000)
)
),
DoubleWidth<DoubleWidth<DoubleWidth<UInt64>>>(
DoubleWidth<DoubleWidth<UInt64>>(
DoubleWidth<UInt64>(0x0000000000000000, 0x0000000000000000),
DoubleWidth<UInt64>(0x0000000000000000, 0x000000000004c6d7)
Digest512(
Digest256(
Digest128(0x0000000000000000, 0x0000000000000000),
Digest128(0x0000000000000000, 0x000000000004c6d7)
),
DoubleWidth<DoubleWidth<UInt64>>(
DoubleWidth<UInt64>(0xeb6e73802734510a, 0x555f256cc005ae55),
DoubleWidth<UInt64>(0x6bde8cc9c6a93b21, 0xaff4b16c71ee90b3)
Digest256(
Digest128(0xeb6e73802734510a, 0x555f256cc005ae55),
Digest128(0x6bde8cc9c6a93b21, 0xaff4b16c71ee90b3)
)
)
)
}
}


/// A `FNV-1` hasher with a `DoubleWidth<DoubleWidth<DoubleWidth<UInt64>>>` digest.
/// A `FNV-1` hasher with a `Digest1024` digest.
public struct FNV1024: FNVHasher {
public private(set) var digest: DoubleWidth<DoubleWidth<DoubleWidth<DoubleWidth<UInt64>>>>
public private(set) var digest: Digest1024

public init() {
self.digest = .fnvOffset
Expand Down Expand Up @@ -88,9 +95,9 @@ public struct FNV1024: FNVHasher {
}
}

/// A `FNV-1a` hasher with a `DoubleWidth<DoubleWidth<DoubleWidth<UInt64>>>` digest.
/// A `FNV-1a` hasher with a `Digest1024` digest.
public struct FNV1024a: FNVHasher {
public private(set) var digest: DoubleWidth<DoubleWidth<DoubleWidth<DoubleWidth<UInt64>>>>
public private(set) var digest: Digest1024

public init() {
self.digest = .fnvOffset
Expand Down
25 changes: 16 additions & 9 deletions Sources/FowlerNollVo/Hashers/FNV128.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,26 @@
// Created by Christopher Richez on 2/2/22.
//

extension DoubleWidth where Base == UInt64 {
fileprivate static var fnvPrime: DoubleWidth<UInt64> {
DoubleWidth<UInt64>(0x0000000001000000, 0x000000000000013B)
/// A 128-bit digest that behaves like an unsigned integer.
///
/// `DoubleWidth` is from the [Swift numerics](https://github.com/apple/swift-numerics)
/// open-source project. Expect all operations on a `Digest128` value to perform slighly worse
/// than operations on `UInt64` and other standard library native integers.
public typealias Digest128 = DoubleWidth<UInt64>

extension Digest128 {
fileprivate static var fnvPrime: Digest128 {
Digest128(0x0000000001000000, 0x000000000000013B)
}

fileprivate static var fnvOffset: DoubleWidth<UInt64> {
DoubleWidth<UInt64>(0x6c62272e07bb0142, 0x62b821756295c58d)
fileprivate static var fnvOffset: Digest128 {
Digest128(0x6c62272e07bb0142, 0x62b821756295c58d)
}
}

/// A `FNV-1` hasher with a `DoubleWidth<UInt64>` digest.
/// A `FNV-1` hasher with a `Digest128` digest.
public struct FNV128: FNVHasher {
public private(set) var digest: DoubleWidth<UInt64>
public private(set) var digest: Digest128

public init() {
self.digest = .fnvOffset
Expand Down Expand Up @@ -45,9 +52,9 @@ public struct FNV128: FNVHasher {
}
}

/// A `FNV-1a` hasher with a `DoubleWidth<UInt64>` digest.
/// A `FNV-1a` hasher with a `Digest128` digest.
public struct FNV128a: FNVHasher {
public private(set) var digest: DoubleWidth<UInt64>
public private(set) var digest: Digest128

public init() {
self.digest = .fnvOffset
Expand Down
34 changes: 21 additions & 13 deletions Sources/FowlerNollVo/Hashers/FNV256.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,32 @@
// Created by Christopher Richez on 2/2/22.
//

extension DoubleWidth where Base == DoubleWidth<UInt64> {
fileprivate static var fnvPrime: DoubleWidth<DoubleWidth<UInt64>> {
DoubleWidth<DoubleWidth<UInt64>>(
DoubleWidth<UInt64>(0x0000000000000000, 0x0000010000000000),
DoubleWidth<UInt64>(0x0000000000000000, 0x0000000000000163)
/// A 256-bit digest that behaves like an unsigned integer.
///
/// `DoubleWidth` is from the [Swift numerics](https://github.com/apple/swift-numerics)
/// open-source project. Expect all operations on a `Digest256` value to perform slighly worse
/// than operations on `UInt64` and other standard library native integers.
public typealias Digest256 = DoubleWidth<Digest128>

extension Digest256 {
fileprivate static var fnvPrime: Digest256 {
Digest256(
Digest128(0x0000000000000000, 0x0000010000000000),
Digest128(0x0000000000000000, 0x0000000000000163)
)
}
fileprivate static var fnvOffset: DoubleWidth<DoubleWidth<UInt64>> {
DoubleWidth<DoubleWidth<UInt64>>(
DoubleWidth<UInt64>(0xdd268dbcaac55036, 0x2d98c384c4e576cc),
DoubleWidth<UInt64>(0xc8b1536847b6bbb3, 0x1023b4c8caee0535)

fileprivate static var fnvOffset: Digest256 {
Digest256(
Digest128(0xdd268dbcaac55036, 0x2d98c384c4e576cc),
Digest128(0xc8b1536847b6bbb3, 0x1023b4c8caee0535)
)
}
}

/// A `FNV-1` hasher with a `DoubleWidth<DoubleWidth<UInt64>>` digest.
/// A `FNV-1` hasher with a `Digest256` digest.
public struct FNV256: FNVHasher {
public private(set) var digest: DoubleWidth<DoubleWidth<UInt64>>
public private(set) var digest: Digest256

public init() {
self.digest = .fnvOffset
Expand Down Expand Up @@ -50,9 +58,9 @@ public struct FNV256: FNVHasher {
}
}

/// A `FNV-1a` hasher with a `DoubleWidth<DoubleWidth<UInt64>>` digest.
/// A `FNV-1a` hasher with a `Digest256` digest.
public struct FNV256a: FNVHasher {
public private(set) var digest: DoubleWidth<DoubleWidth<UInt64>>
public private(set) var digest: Digest256

public init() {
self.digest = .fnvOffset
Expand Down
49 changes: 28 additions & 21 deletions Sources/FowlerNollVo/Hashers/FNV512.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,38 +5,45 @@
// Created by Christopher Richez on 2/2/22.
//

extension DoubleWidth where Base == DoubleWidth<DoubleWidth<UInt64>> {
fileprivate static var fnvPrime: DoubleWidth<DoubleWidth<DoubleWidth<UInt64>>> {
DoubleWidth<DoubleWidth<DoubleWidth<UInt64>>>(
DoubleWidth<DoubleWidth<UInt64>>(
DoubleWidth<UInt64>(0x0000000000000000, 0x0000000000000000),
DoubleWidth<UInt64>(0x0000000001000000, 0x0000000000000000)
/// A 512-bit digest that behaves like an unsigned integer.
///
/// `DoubleWidth` is from the [Swift numerics](https://github.com/apple/swift-numerics)
/// open-source project. Expect all operations on a `Digest512` value to perform
/// noticeably worse than operations on `UInt64` and other standard library native integers.
public typealias Digest512 = DoubleWidth<Digest256>

extension Digest512 {
fileprivate static var fnvPrime: Digest512 {
Digest512(
Digest256(
Digest128(0x0000000000000000, 0x0000000000000000),
Digest128(0x0000000001000000, 0x0000000000000000)
),
DoubleWidth<DoubleWidth<UInt64>>(
DoubleWidth<UInt64>(0x0000000000000000, 0x0000000000000000),
DoubleWidth<UInt64>(0x0000000000000000, 0x0000000000000157)
Digest256(
Digest128(0x0000000000000000, 0x0000000000000000),
Digest128(0x0000000000000000, 0x0000000000000157)
)
)
}

fileprivate static var fnvOffset: DoubleWidth<DoubleWidth<DoubleWidth<UInt64>>> {
DoubleWidth<DoubleWidth<DoubleWidth<UInt64>>>(
DoubleWidth<DoubleWidth<UInt64>>(
DoubleWidth<UInt64>(0xb86db0b1171f4416, 0xdca1e50f309990ac),
DoubleWidth<UInt64>(0xac87d059c9000000, 0x0000000000000d21)
fileprivate static var fnvOffset: Digest512 {
Digest512(
Digest256(
Digest128(0xb86db0b1171f4416, 0xdca1e50f309990ac),
Digest128(0xac87d059c9000000, 0x0000000000000d21)
),
DoubleWidth<DoubleWidth<UInt64>>(
DoubleWidth<UInt64>(0xe948f68a34c192f6, 0x2ea79bc942dbe7ce),
DoubleWidth<UInt64>(0x182036415f56e34b, 0xac982aac4afe9fd9)
Digest256(
Digest128(0xe948f68a34c192f6, 0x2ea79bc942dbe7ce),
Digest128(0x182036415f56e34b, 0xac982aac4afe9fd9)
)
)
}
}


/// A `FNV-1` hasher with a `DoubleWidth<DoubleWidth<DoubleWidth<UInt64>>>` digest.
/// A `FNV-1` hasher with a `Digest512` digest.
public struct FNV512: FNVHasher {
public private(set) var digest: DoubleWidth<DoubleWidth<DoubleWidth<UInt64>>>
public private(set) var digest: Digest512

public init() {
self.digest = .fnvOffset
Expand Down Expand Up @@ -64,9 +71,9 @@ public struct FNV512: FNVHasher {
}
}

/// A `FNV-1a` hasher with a `DoubleWidth<DoubleWidth<DoubleWidth<UInt64>>>` digest.
/// A `FNV-1a` hasher with a `Digest512` digest.
public struct FNV512a: FNVHasher {
public private(set) var digest: DoubleWidth<DoubleWidth<DoubleWidth<UInt64>>>
public private(set) var digest: Digest512

public init() {
self.digest = .fnvOffset
Expand Down
20 changes: 19 additions & 1 deletion Tests/FowlerNollVoTests/HashStabilityTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ class HashStabilityTests: XCTestCase {
}
}

/// An array of randomly generated `FNVHashable` values.
let inputs: [FNVHashable] = [
"this is a test \u{10424}", String?.none,
Bool.random(), Bool?.none,
Expand Down Expand Up @@ -80,4 +79,23 @@ class HashStabilityTests: XCTestCase {
checkStability(of: input, withHasher: FNV1024a.self)
}
}

#if swift(>=5.4) && !((os(macOS) || targetEnvironment(macCatalyst)) && arch(x86_64))
@available(macOS 11, iOS 14, tvOS 14, watchOS 7, *)
func testFloat16() {
let input = Float16.random(in: .leastNonzeroMagnitude ... .greatestFiniteMagnitude)
checkStability(of: input, withHasher: FNV32a.self)
checkStability(of: input, withHasher: FNV64.self)
checkStability(of: input, withHasher: FNV64a.self)
checkStability(of: input, withHasher: FNV128.self)
checkStability(of: input, withHasher: FNV128a.self)
checkStability(of: input, withHasher: FNV256.self)
checkStability(of: input, withHasher: FNV256a.self)
checkStability(of: input, withHasher: FNV512.self)
checkStability(of: input, withHasher: FNV512a.self)
checkStability(of: input, withHasher: FNV1024.self)
checkStability(of: input, withHasher: FNV1024a.self)
}
#endif

}

0 comments on commit 2ec3c78

Please sign in to comment.