diff --git a/README.md b/README.md index d13095e..f372130 100644 --- a/README.md +++ b/README.md @@ -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.** @@ -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 diff --git a/Sources/FowlerNollVo/FNVHashable.swift b/Sources/FowlerNollVo/FNVHashable.swift index 68625c3..1836e99 100644 --- a/Sources/FowlerNollVo/FNVHashable.swift +++ b/Sources/FowlerNollVo/FNVHashable.swift @@ -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(into hasher: inout Hasher) where Hasher : FNVHasher { hasher.combine(isZero ? Float16.zero.bitPattern : bitPattern) diff --git a/Sources/FowlerNollVo/Hashers/FNV1024.swift b/Sources/FowlerNollVo/Hashers/FNV1024.swift index 3e386f2..cea2eff 100644 --- a/Sources/FowlerNollVo/Hashers/FNV1024.swift +++ b/Sources/FowlerNollVo/Hashers/FNV1024.swift @@ -5,52 +5,59 @@ // Created by Christopher Richez on 2/2/22. // -extension DoubleWidth where Base == DoubleWidth>> { - fileprivate static var fnvPrime: DoubleWidth>>> { - DoubleWidth>>>( - DoubleWidth>>( - DoubleWidth>( - DoubleWidth(0x0000000000000000, 0x0000000000000000), - DoubleWidth(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 + +extension Digest1024 { + fileprivate static var fnvPrime: Digest1024 { + Digest1024( + Digest512( + Digest256( + Digest128(0x0000000000000000, 0x0000000000000000), + Digest128(0x0000000000000000, 0x0000000000000000) ), - DoubleWidth>( - DoubleWidth(0x0000000000000000, 0x0000010000000000), - DoubleWidth(0x0000000000000000, 0x0000000000000000) + Digest256( + Digest128(0x0000000000000000, 0x0000010000000000), + Digest128(0x0000000000000000, 0x0000000000000000) ) ), - DoubleWidth>>( - DoubleWidth>( - DoubleWidth(0x0000000000000000, 0x0000000000000000), - DoubleWidth(0x0000000000000000, 0x0000000000000000) + Digest512( + Digest256( + Digest128(0x0000000000000000, 0x0000000000000000), + Digest128(0x0000000000000000, 0x0000000000000000) ), - DoubleWidth>( - DoubleWidth(0x0000000000000000, 0x0000000000000000), - DoubleWidth(0x0000000000000000, 0x000000000000018D) + Digest256( + Digest128(0x0000000000000000, 0x0000000000000000), + Digest128(0x0000000000000000, 0x000000000000018D) ) ) ) } - fileprivate static var fnvOffset: DoubleWidth>>> { - DoubleWidth>>>( - DoubleWidth>>( - DoubleWidth>( - DoubleWidth(0x0000000000000000, 0x005f7a76758ecc4d), - DoubleWidth(0x32e56d5a591028b7, 0x4b29fc4223fdada1) + fileprivate static var fnvOffset: Digest1024 { + Digest1024( + Digest512( + Digest256( + Digest128(0x0000000000000000, 0x005f7a76758ecc4d), + Digest128(0x32e56d5a591028b7, 0x4b29fc4223fdada1) ), - DoubleWidth>( - DoubleWidth(0x6c3bf34eda3674da, 0x9a21d90000000000), - DoubleWidth(0x0000000000000000, 0x0000000000000000) + Digest256( + Digest128(0x6c3bf34eda3674da, 0x9a21d90000000000), + Digest128(0x0000000000000000, 0x0000000000000000) ) ), - DoubleWidth>>( - DoubleWidth>( - DoubleWidth(0x0000000000000000, 0x0000000000000000), - DoubleWidth(0x0000000000000000, 0x000000000004c6d7) + Digest512( + Digest256( + Digest128(0x0000000000000000, 0x0000000000000000), + Digest128(0x0000000000000000, 0x000000000004c6d7) ), - DoubleWidth>( - DoubleWidth(0xeb6e73802734510a, 0x555f256cc005ae55), - DoubleWidth(0x6bde8cc9c6a93b21, 0xaff4b16c71ee90b3) + Digest256( + Digest128(0xeb6e73802734510a, 0x555f256cc005ae55), + Digest128(0x6bde8cc9c6a93b21, 0xaff4b16c71ee90b3) ) ) ) @@ -58,9 +65,9 @@ extension DoubleWidth where Base == DoubleWidth> } -/// A `FNV-1` hasher with a `DoubleWidth>>` digest. +/// A `FNV-1` hasher with a `Digest1024` digest. public struct FNV1024: FNVHasher { - public private(set) var digest: DoubleWidth>>> + public private(set) var digest: Digest1024 public init() { self.digest = .fnvOffset @@ -88,9 +95,9 @@ public struct FNV1024: FNVHasher { } } -/// A `FNV-1a` hasher with a `DoubleWidth>>` digest. +/// A `FNV-1a` hasher with a `Digest1024` digest. public struct FNV1024a: FNVHasher { - public private(set) var digest: DoubleWidth>>> + public private(set) var digest: Digest1024 public init() { self.digest = .fnvOffset diff --git a/Sources/FowlerNollVo/Hashers/FNV128.swift b/Sources/FowlerNollVo/Hashers/FNV128.swift index 4554c94..2f07217 100644 --- a/Sources/FowlerNollVo/Hashers/FNV128.swift +++ b/Sources/FowlerNollVo/Hashers/FNV128.swift @@ -5,19 +5,26 @@ // Created by Christopher Richez on 2/2/22. // -extension DoubleWidth where Base == UInt64 { - fileprivate static var fnvPrime: DoubleWidth { - DoubleWidth(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 + +extension Digest128 { + fileprivate static var fnvPrime: Digest128 { + Digest128(0x0000000001000000, 0x000000000000013B) } - fileprivate static var fnvOffset: DoubleWidth { - DoubleWidth(0x6c62272e07bb0142, 0x62b821756295c58d) + fileprivate static var fnvOffset: Digest128 { + Digest128(0x6c62272e07bb0142, 0x62b821756295c58d) } } -/// A `FNV-1` hasher with a `DoubleWidth` digest. +/// A `FNV-1` hasher with a `Digest128` digest. public struct FNV128: FNVHasher { - public private(set) var digest: DoubleWidth + public private(set) var digest: Digest128 public init() { self.digest = .fnvOffset @@ -45,9 +52,9 @@ public struct FNV128: FNVHasher { } } -/// A `FNV-1a` hasher with a `DoubleWidth` digest. +/// A `FNV-1a` hasher with a `Digest128` digest. public struct FNV128a: FNVHasher { - public private(set) var digest: DoubleWidth + public private(set) var digest: Digest128 public init() { self.digest = .fnvOffset diff --git a/Sources/FowlerNollVo/Hashers/FNV256.swift b/Sources/FowlerNollVo/Hashers/FNV256.swift index 870e090..1d8632e 100644 --- a/Sources/FowlerNollVo/Hashers/FNV256.swift +++ b/Sources/FowlerNollVo/Hashers/FNV256.swift @@ -5,24 +5,32 @@ // Created by Christopher Richez on 2/2/22. // -extension DoubleWidth where Base == DoubleWidth { - fileprivate static var fnvPrime: DoubleWidth> { - DoubleWidth>( - DoubleWidth(0x0000000000000000, 0x0000010000000000), - DoubleWidth(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 + +extension Digest256 { + fileprivate static var fnvPrime: Digest256 { + Digest256( + Digest128(0x0000000000000000, 0x0000010000000000), + Digest128(0x0000000000000000, 0x0000000000000163) ) } - fileprivate static var fnvOffset: DoubleWidth> { - DoubleWidth>( - DoubleWidth(0xdd268dbcaac55036, 0x2d98c384c4e576cc), - DoubleWidth(0xc8b1536847b6bbb3, 0x1023b4c8caee0535) + + fileprivate static var fnvOffset: Digest256 { + Digest256( + Digest128(0xdd268dbcaac55036, 0x2d98c384c4e576cc), + Digest128(0xc8b1536847b6bbb3, 0x1023b4c8caee0535) ) } } -/// A `FNV-1` hasher with a `DoubleWidth>` digest. +/// A `FNV-1` hasher with a `Digest256` digest. public struct FNV256: FNVHasher { - public private(set) var digest: DoubleWidth> + public private(set) var digest: Digest256 public init() { self.digest = .fnvOffset @@ -50,9 +58,9 @@ public struct FNV256: FNVHasher { } } -/// A `FNV-1a` hasher with a `DoubleWidth>` digest. +/// A `FNV-1a` hasher with a `Digest256` digest. public struct FNV256a: FNVHasher { - public private(set) var digest: DoubleWidth> + public private(set) var digest: Digest256 public init() { self.digest = .fnvOffset diff --git a/Sources/FowlerNollVo/Hashers/FNV512.swift b/Sources/FowlerNollVo/Hashers/FNV512.swift index 792c602..483d6df 100644 --- a/Sources/FowlerNollVo/Hashers/FNV512.swift +++ b/Sources/FowlerNollVo/Hashers/FNV512.swift @@ -5,38 +5,45 @@ // Created by Christopher Richez on 2/2/22. // -extension DoubleWidth where Base == DoubleWidth> { - fileprivate static var fnvPrime: DoubleWidth>> { - DoubleWidth>>( - DoubleWidth>( - DoubleWidth(0x0000000000000000, 0x0000000000000000), - DoubleWidth(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 + +extension Digest512 { + fileprivate static var fnvPrime: Digest512 { + Digest512( + Digest256( + Digest128(0x0000000000000000, 0x0000000000000000), + Digest128(0x0000000001000000, 0x0000000000000000) ), - DoubleWidth>( - DoubleWidth(0x0000000000000000, 0x0000000000000000), - DoubleWidth(0x0000000000000000, 0x0000000000000157) + Digest256( + Digest128(0x0000000000000000, 0x0000000000000000), + Digest128(0x0000000000000000, 0x0000000000000157) ) ) } - fileprivate static var fnvOffset: DoubleWidth>> { - DoubleWidth>>( - DoubleWidth>( - DoubleWidth(0xb86db0b1171f4416, 0xdca1e50f309990ac), - DoubleWidth(0xac87d059c9000000, 0x0000000000000d21) + fileprivate static var fnvOffset: Digest512 { + Digest512( + Digest256( + Digest128(0xb86db0b1171f4416, 0xdca1e50f309990ac), + Digest128(0xac87d059c9000000, 0x0000000000000d21) ), - DoubleWidth>( - DoubleWidth(0xe948f68a34c192f6, 0x2ea79bc942dbe7ce), - DoubleWidth(0x182036415f56e34b, 0xac982aac4afe9fd9) + Digest256( + Digest128(0xe948f68a34c192f6, 0x2ea79bc942dbe7ce), + Digest128(0x182036415f56e34b, 0xac982aac4afe9fd9) ) ) } } -/// A `FNV-1` hasher with a `DoubleWidth>>` digest. +/// A `FNV-1` hasher with a `Digest512` digest. public struct FNV512: FNVHasher { - public private(set) var digest: DoubleWidth>> + public private(set) var digest: Digest512 public init() { self.digest = .fnvOffset @@ -64,9 +71,9 @@ public struct FNV512: FNVHasher { } } -/// A `FNV-1a` hasher with a `DoubleWidth>>` digest. +/// A `FNV-1a` hasher with a `Digest512` digest. public struct FNV512a: FNVHasher { - public private(set) var digest: DoubleWidth>> + public private(set) var digest: Digest512 public init() { self.digest = .fnvOffset diff --git a/Tests/FowlerNollVoTests/HashStabilityTests.swift b/Tests/FowlerNollVoTests/HashStabilityTests.swift index 0503f1b..c744ad7 100644 --- a/Tests/FowlerNollVoTests/HashStabilityTests.swift +++ b/Tests/FowlerNollVoTests/HashStabilityTests.swift @@ -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, @@ -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 + }