From ccdc218cbd2bbf5a0897dbe4f3f1016e999a9e58 Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Fri, 13 Apr 2018 20:20:09 +0100 Subject: [PATCH 1/3] [stdlib] _Hasher: append => combine --- stdlib/public/core/Arrays.swift.gyb | 2 +- stdlib/public/core/Bool.swift | 2 +- stdlib/public/core/CTypes.swift | 2 +- stdlib/public/core/ClosedRange.swift | 10 ++--- stdlib/public/core/Dictionary.swift | 10 ++--- stdlib/public/core/DropWhile.swift | 2 +- stdlib/public/core/Flatten.swift | 4 +- .../public/core/FloatingPointTypes.swift.gyb | 6 +-- stdlib/public/core/Hashable.swift | 4 +- stdlib/public/core/Hashing.swift | 44 +++++++++---------- stdlib/public/core/Integers.swift.gyb | 6 +-- stdlib/public/core/KeyPath.swift | 38 ++++++++-------- stdlib/public/core/NewtypeWrapper.swift | 2 +- stdlib/public/core/Optional.swift | 6 +-- stdlib/public/core/PrefixWhile.swift | 4 +- stdlib/public/core/Range.swift | 4 +- stdlib/public/core/Reverse.swift | 2 +- stdlib/public/core/Set.swift | 6 +-- stdlib/public/core/SipHash.swift.gyb | 24 +++++----- stdlib/public/core/StringHashable.swift | 22 +++++----- stdlib/public/core/UnsafePointer.swift.gyb | 2 +- test/Prototypes/DoubleWidth.swift.gyb | 4 +- validation-test/stdlib/FixedPoint.swift.gyb | 4 +- validation-test/stdlib/Hashing.swift | 10 ++--- validation-test/stdlib/HashingAvalanche.swift | 4 +- validation-test/stdlib/SipHash.swift | 2 +- 26 files changed, 113 insertions(+), 113 deletions(-) diff --git a/stdlib/public/core/Arrays.swift.gyb b/stdlib/public/core/Arrays.swift.gyb index 340073876908b..e1adb72227d53 100644 --- a/stdlib/public/core/Arrays.swift.gyb +++ b/stdlib/public/core/Arrays.swift.gyb @@ -2271,7 +2271,7 @@ extension ${Self}: Hashable where Element: Hashable { @inlinable // FIXME(sil-serialize-all) public func _hash(into hasher: inout _Hasher) { for element in self { - hasher.append(element) + hasher.combine(element) } } } diff --git a/stdlib/public/core/Bool.swift b/stdlib/public/core/Bool.swift index cac0baae48ca4..12d801df89b01 100644 --- a/stdlib/public/core/Bool.swift +++ b/stdlib/public/core/Bool.swift @@ -159,7 +159,7 @@ extension Bool : Equatable, Hashable { @inlinable // FIXME(sil-serialize-all) public func _hash(into hasher: inout _Hasher) { - hasher.append((self ? 1 : 0) as UInt8) + hasher.combine((self ? 1 : 0) as UInt8) } @inlinable // FIXME(sil-serialize-all) diff --git a/stdlib/public/core/CTypes.swift b/stdlib/public/core/CTypes.swift index 16f548c6c0391..12d1640cbc8b9 100644 --- a/stdlib/public/core/CTypes.swift +++ b/stdlib/public/core/CTypes.swift @@ -186,7 +186,7 @@ extension OpaquePointer: Hashable { @inlinable // FIXME(sil-serialize-all) public func _hash(into hasher: inout _Hasher) { - hasher.append(Int(Builtin.ptrtoint_Word(_rawValue))) + hasher.combine(Int(Builtin.ptrtoint_Word(_rawValue))) } } diff --git a/stdlib/public/core/ClosedRange.swift b/stdlib/public/core/ClosedRange.swift index df64925397317..8485bc6bddaa6 100644 --- a/stdlib/public/core/ClosedRange.swift +++ b/stdlib/public/core/ClosedRange.swift @@ -177,10 +177,10 @@ where Bound: Strideable, Bound.Stride: SignedInteger, Bound: Hashable { public func _hash(into hasher: inout _Hasher) { switch self { case .inRange(let value): - hasher.append(0 as Int8) - hasher.append(value) + hasher.combine(0 as Int8) + hasher.combine(value) case .pastEnd: - hasher.append(1 as Int8) + hasher.combine(1 as Int8) } } } @@ -396,8 +396,8 @@ extension ClosedRange: Hashable where Bound: Hashable { @inlinable // FIXME(sil-serialize-all) public func _hash(into hasher: inout _Hasher) { - hasher.append(lowerBound) - hasher.append(upperBound) + hasher.combine(lowerBound) + hasher.combine(upperBound) } } diff --git a/stdlib/public/core/Dictionary.swift b/stdlib/public/core/Dictionary.swift index d090266059c9a..ff9e60fed556e 100644 --- a/stdlib/public/core/Dictionary.swift +++ b/stdlib/public/core/Dictionary.swift @@ -1453,11 +1453,11 @@ extension Dictionary: Hashable where Value: Hashable { var commutativeHash = 0 for (k, v) in self { var elementHasher = _Hasher() - elementHasher.append(k) - elementHasher.append(v) + elementHasher.combine(k) + elementHasher.combine(v) commutativeHash ^= elementHasher.finalize() } - hasher.append(commutativeHash) + hasher.combine(commutativeHash) } } @@ -2436,8 +2436,8 @@ extension _NativeDictionaryBuffer where Key: Hashable @inlinable // FIXME(sil-serialize-all) @inline(__always) // For performance reasons. internal func _bucket(_ k: Key) -> Int { - var hasher = _Hasher(seed: _storage.seed) - hasher.append(k) + var hasher = _Hasher(_seed: _storage.seed) + hasher.combine(k) return hasher.finalize() & _bucketMask } diff --git a/stdlib/public/core/DropWhile.swift b/stdlib/public/core/DropWhile.swift index 3f9da279d36dd..1db1252746b7d 100644 --- a/stdlib/public/core/DropWhile.swift +++ b/stdlib/public/core/DropWhile.swift @@ -190,7 +190,7 @@ extension LazyDropWhileCollection.Index: Hashable where Base.Index: Hashable { @inlinable // FIXME(sil-serialize-all) public func _hash(into hasher: inout _Hasher) { - hasher.append(base) + hasher.combine(base) } } diff --git a/stdlib/public/core/Flatten.swift b/stdlib/public/core/Flatten.swift index 88c89eff0912a..fca9bafca8227 100644 --- a/stdlib/public/core/Flatten.swift +++ b/stdlib/public/core/Flatten.swift @@ -236,8 +236,8 @@ extension FlattenCollection.Index : Hashable @inlinable // FIXME(sil-serialize-all) public func _hash(into hasher: inout _Hasher) { - hasher.append(_outer) - hasher.append(_inner) + hasher.combine(_outer) + hasher.combine(_inner) } } diff --git a/stdlib/public/core/FloatingPointTypes.swift.gyb b/stdlib/public/core/FloatingPointTypes.swift.gyb index 4eb244491c242..b615881571a9e 100644 --- a/stdlib/public/core/FloatingPointTypes.swift.gyb +++ b/stdlib/public/core/FloatingPointTypes.swift.gyb @@ -1539,10 +1539,10 @@ extension ${Self} : Hashable { v = 0 } %if bits == 80: - hasher.append(v._representation.signAndExponent) - hasher.append(v.significandBitPattern) + hasher.combine(v._representation.signAndExponent) + hasher.combine(v.significandBitPattern) %else: - hasher.append(v.bitPattern) + hasher.combine(v.bitPattern) %end } diff --git a/stdlib/public/core/Hashable.swift b/stdlib/public/core/Hashable.swift index 0be9f2dd2a5a5..9a6312b641565 100644 --- a/stdlib/public/core/Hashable.swift +++ b/stdlib/public/core/Hashable.swift @@ -116,7 +116,7 @@ public protocol Hashable : Equatable { extension Hashable { @inline(__always) public func _hash(into hasher: inout _Hasher) { - hasher.append(self.hashValue) + hasher.combine(self.hashValue) } } @@ -124,7 +124,7 @@ extension Hashable { @inline(__always) public func _hashValue(for value: H) -> Int { var hasher = _Hasher() - hasher.append(value) + hasher.combine(value) return hasher.finalize() } diff --git a/stdlib/public/core/Hashing.swift b/stdlib/public/core/Hashing.swift index 9b5a53b864d54..1bb8c43a1d033 100644 --- a/stdlib/public/core/Hashing.swift +++ b/stdlib/public/core/Hashing.swift @@ -165,26 +165,26 @@ internal struct _LegacyHasher { } @inline(__always) - internal mutating func append(_ value: Int) { + internal mutating func combine(_ value: Int) { _hash = (_hash == 0 ? value : _combineHashValues(_hash, value)) } @inline(__always) - internal mutating func append(_ value: UInt) { - append(Int(bitPattern: value)) + internal mutating func combine(_ value: UInt) { + combine(Int(bitPattern: value)) } @inline(__always) - internal mutating func append(_ value: UInt32) { - append(Int(truncatingIfNeeded: value)) + internal mutating func combine(_ value: UInt32) { + combine(Int(truncatingIfNeeded: value)) } @inline(__always) - internal mutating func append(_ value: UInt64) { + internal mutating func combine(_ value: UInt64) { if UInt64.bitWidth > Int.bitWidth { - append(Int(truncatingIfNeeded: value ^ (value &>> 32))) + combine(Int(truncatingIfNeeded: value ^ (value &>> 32))) } else { - append(Int(truncatingIfNeeded: value)) + combine(Int(truncatingIfNeeded: value)) } } @@ -212,7 +212,7 @@ public struct _Hasher { // NOT @inlinable @effects(releasenone) - public init(seed: (UInt64, UInt64)) { + public init(_seed seed: (UInt64, UInt64)) { self._core = Core(key: seed) } @@ -250,40 +250,40 @@ public struct _Hasher { @inlinable @inline(__always) - public mutating func append(_ value: H) { + public mutating func combine(_ value: H) { value._hash(into: &self) } // NOT @inlinable @effects(releasenone) - public mutating func append(bits: UInt) { - _core.append(bits) + public mutating func combine(bits: UInt) { + _core.combine(bits) } // NOT @inlinable @effects(releasenone) - public mutating func append(bits: UInt32) { - _core.append(bits) + public mutating func combine(bits: UInt32) { + _core.combine(bits) } // NOT @inlinable @effects(releasenone) - public mutating func append(bits: UInt64) { - _core.append(bits) + public mutating func combine(bits: UInt64) { + _core.combine(bits) } // NOT @inlinable @effects(releasenone) - public mutating func append(bits: Int) { - _core.append(UInt(bitPattern: bits)) + public mutating func combine(bits: Int) { + _core.combine(UInt(bitPattern: bits)) } // NOT @inlinable @effects(releasenone) - public mutating func append(bits: Int32) { - _core.append(UInt32(bitPattern: bits)) + public mutating func combine(bits: Int32) { + _core.combine(UInt32(bitPattern: bits)) } // NOT @inlinable @effects(releasenone) - public mutating func append(bits: Int64) { - _core.append(UInt64(bitPattern: bits)) + public mutating func combine(bits: Int64) { + _core.combine(UInt64(bitPattern: bits)) } // NOT @inlinable diff --git a/stdlib/public/core/Integers.swift.gyb b/stdlib/public/core/Integers.swift.gyb index 0389bf7a2cc30..f0f4fd766c163 100644 --- a/stdlib/public/core/Integers.swift.gyb +++ b/stdlib/public/core/Integers.swift.gyb @@ -3715,12 +3715,12 @@ extension ${Self} : Hashable { // this, we should introduce a custom AnyHashable box for integer values // that sign-extends values to 64 bits. % if bits <= word_bits: - hasher.append(bits: _lowWord) + hasher.combine(bits: _lowWord) % elif bits == 2 * word_bits: if let word = ${"" if signed else "U"}Int(exactly: self) { - hasher.append(bits: word._lowWord) + hasher.combine(bits: word._lowWord) } else { - hasher.append(bits: UInt64(_value)) + hasher.combine(bits: UInt64(_value)) } % else: fatalError("Unsupported integer width") diff --git a/stdlib/public/core/KeyPath.swift b/stdlib/public/core/KeyPath.swift index 95747c5e17eba..8e531d3eadb2b 100644 --- a/stdlib/public/core/KeyPath.swift +++ b/stdlib/public/core/KeyPath.swift @@ -57,9 +57,9 @@ public class AnyKeyPath: Hashable, _AppendKeyPath { var buffer = $0 while true { let (component, type) = buffer.next() - hasher.append(component.value) + hasher.combine(component.value) if let type = type { - hasher.append(unsafeBitCast(type, to: Int.self)) + hasher.combine(unsafeBitCast(type, to: Int.self)) } else { break } @@ -447,9 +447,9 @@ internal struct ComputedPropertyID: Hashable { @inlinable // FIXME(sil-serialize-all) public func _hash(into hasher: inout _Hasher) { - hasher.append(value) - hasher.append(isStoredProperty) - hasher.append(isTableOffset) + hasher.combine(value) + hasher.combine(isStoredProperty) + hasher.combine(isTableOffset) } } @@ -592,34 +592,34 @@ internal enum KeyPathComponent: Hashable { // hash value of the overall key path. // FIXME(hasher): hash witness should just mutate hasher directly if hash != 0 { - hasher.append(hash) + hasher.combine(hash) } } } switch self { case .struct(offset: let a): - hasher.append(0) - hasher.append(a) + hasher.combine(0) + hasher.combine(a) case .class(offset: let b): - hasher.append(1) - hasher.append(b) + hasher.combine(1) + hasher.combine(b) case .optionalChain: - hasher.append(2) + hasher.combine(2) case .optionalForce: - hasher.append(3) + hasher.combine(3) case .optionalWrap: - hasher.append(4) + hasher.combine(4) case .get(id: let id, get: _, argument: let argument): - hasher.append(5) - hasher.append(id) + hasher.combine(5) + hasher.combine(id) appendHashFromArgument(argument) case .mutatingGetSet(id: let id, get: _, set: _, argument: let argument): - hasher.append(6) - hasher.append(id) + hasher.combine(6) + hasher.combine(id) appendHashFromArgument(argument) case .nonmutatingGetSet(id: let id, get: _, set: _, argument: let argument): - hasher.append(7) - hasher.append(id) + hasher.combine(7) + hasher.combine(id) appendHashFromArgument(argument) } } diff --git a/stdlib/public/core/NewtypeWrapper.swift b/stdlib/public/core/NewtypeWrapper.swift index 08aa9f986f2b4..a9824db46123f 100644 --- a/stdlib/public/core/NewtypeWrapper.swift +++ b/stdlib/public/core/NewtypeWrapper.swift @@ -23,7 +23,7 @@ extension _SwiftNewtypeWrapper where Self: Hashable, Self.RawValue : Hashable { @inlinable // FIXME(sil-serialize-all) public func _hash(into hasher: inout _Hasher) { - hasher.append(rawValue) + hasher.combine(rawValue) } } diff --git a/stdlib/public/core/Optional.swift b/stdlib/public/core/Optional.swift index 7409982127406..e5250ddc1a29b 100644 --- a/stdlib/public/core/Optional.swift +++ b/stdlib/public/core/Optional.swift @@ -425,10 +425,10 @@ extension Optional: Hashable where Wrapped: Hashable { public func _hash(into hasher: inout _Hasher) { switch self { case .none: - hasher.append(0 as UInt8) + hasher.combine(0 as UInt8) case .some(let wrapped): - hasher.append(1 as UInt8) - hasher.append(wrapped) + hasher.combine(1 as UInt8) + hasher.combine(wrapped) } } } diff --git a/stdlib/public/core/PrefixWhile.swift b/stdlib/public/core/PrefixWhile.swift index 95adf3c1bb47f..67d54468b283d 100644 --- a/stdlib/public/core/PrefixWhile.swift +++ b/stdlib/public/core/PrefixWhile.swift @@ -211,9 +211,9 @@ extension LazyPrefixWhileCollection.Index: Hashable where Base.Index: Hashable { public func _hash(into hasher: inout _Hasher) { switch _value { case .index(let value): - hasher.append(value) + hasher.combine(value) case .pastEnd: - hasher.append(Int.max) + hasher.combine(Int.max) } } } diff --git a/stdlib/public/core/Range.swift b/stdlib/public/core/Range.swift index b7aac8d1e9020..56c227e9f71b3 100644 --- a/stdlib/public/core/Range.swift +++ b/stdlib/public/core/Range.swift @@ -406,8 +406,8 @@ extension Range: Hashable where Bound: Hashable { @inlinable // FIXME(sil-serialize-all) public func _hash(into hasher: inout _Hasher) { - hasher.append(lowerBound) - hasher.append(upperBound) + hasher.combine(lowerBound) + hasher.combine(upperBound) } } diff --git a/stdlib/public/core/Reverse.swift b/stdlib/public/core/Reverse.swift index db172e7e58669..97aa7bfd558bc 100644 --- a/stdlib/public/core/Reverse.swift +++ b/stdlib/public/core/Reverse.swift @@ -198,7 +198,7 @@ extension ReversedCollection.Index: Hashable where Base.Index: Hashable { @inlinable // FIXME(sil-serialize-all) public func _hash(into hasher: inout _Hasher) { - hasher.append(base) + hasher.combine(base) } } diff --git a/stdlib/public/core/Set.swift b/stdlib/public/core/Set.swift index 2fd512cd6fe3b..e37f85c27ab3f 100644 --- a/stdlib/public/core/Set.swift +++ b/stdlib/public/core/Set.swift @@ -494,7 +494,7 @@ extension Set: Hashable { for member in self { hash ^= _hashValue(for: member) } - hasher.append(hash) + hasher.combine(hash) } } @@ -2041,8 +2041,8 @@ extension _NativeSetBuffer where Element: Hashable @inlinable // FIXME(sil-serialize-all) @inline(__always) // For performance reasons. internal func _bucket(_ k: Key) -> Int { - var hasher = _Hasher(seed: _storage.seed) - hasher.append(k) + var hasher = _Hasher(_seed: _storage.seed) + hasher.combine(k) return hasher.finalize() & _bucketMask } diff --git a/stdlib/public/core/SipHash.swift.gyb b/stdlib/public/core/SipHash.swift.gyb index 7f812c3f85993..eb4ba274696d9 100644 --- a/stdlib/public/core/SipHash.swift.gyb +++ b/stdlib/public/core/SipHash.swift.gyb @@ -80,7 +80,7 @@ struct ${Self} { /// This value holds the byte count and the pending bytes that haven't been /// compressed yet, in the format that the finalization step needs. (The least /// significant 56 bits hold the trailing bytes, while the most significant 8 - /// bits hold the count of bytes appended so far, mod 256.) + /// bits hold the count of bytes combined so far, mod 256.) @usableFromInline internal var tailAndByteCount: UInt64 = 0 @@ -119,28 +119,28 @@ struct ${Self} { } @inline(__always) - public mutating func append(_ value: Int) { - append(UInt(bitPattern: value)) + public mutating func combine(_ value: Int) { + combine(UInt(bitPattern: value)) } @inline(__always) - public mutating func append(_ value: UInt) { + public mutating func combine(_ value: UInt) { % if word_bits == 64: - append(UInt64(_truncatingBits: value._lowWord)) + combine(UInt64(_truncatingBits: value._lowWord)) % elif word_bits == 32: - append(UInt32(_truncatingBits: value._lowWord)) + combine(UInt32(_truncatingBits: value._lowWord)) % else: fatalError("Unsupported word width") % end } @inline(__always) - public mutating func append(_ value: Int32) { - append(UInt32(bitPattern: value)) + public mutating func combine(_ value: Int32) { + combine(UInt32(bitPattern: value)) } @inline(__always) - public mutating func append(_ value: UInt32) { + public mutating func combine(_ value: UInt32) { let m = UInt64(_truncatingBits: value._lowWord) if byteCount & 4 == 0 { _sanityCheck(byteCount & 7 == 0 && tail == 0) @@ -153,12 +153,12 @@ struct ${Self} { } @inline(__always) - public mutating func append(_ value: Int64) { - append(UInt64(bitPattern: value)) + public mutating func combine(_ value: Int64) { + combine(UInt64(bitPattern: value)) } @inline(__always) - public mutating func append(_ m: UInt64) { + public mutating func combine(_ m: UInt64) { if byteCount & 4 == 0 { _sanityCheck(byteCount & 7 == 0 && tail == 0) _compress(m) diff --git a/stdlib/public/core/StringHashable.swift b/stdlib/public/core/StringHashable.swift index c8a249271c99a..f0f25338e1ff1 100644 --- a/stdlib/public/core/StringHashable.swift +++ b/stdlib/public/core/StringHashable.swift @@ -54,13 +54,13 @@ extension _UnmanagedString where CodeUnit == UInt8 { internal func hashASCII(into hasher: inout _Hasher) { var asciiHasher = ASCIIHasher() for c in self { - if let combined = asciiHasher.append(UInt8(truncatingIfNeeded: c)) { - hasher.append(combined) + if let chunk = asciiHasher.append(UInt8(truncatingIfNeeded: c)) { + hasher.combine(chunk) } } - if let combined = asciiHasher.consume() { - hasher.append(combined) + if let chunk = asciiHasher.consume() { + hasher.combine(chunk) } } } @@ -76,26 +76,26 @@ extension BidirectionalCollection where Element == UInt16, SubSequence == Self { let isSingleSegmentScalar = self.hasNormalizationBoundary(after: i) guard cuIsASCII && isSingleSegmentScalar else { - if let combined = asciiHasher.consume() { - hasher.append(combined) + if let chunk = asciiHasher.consume() { + hasher.combine(chunk) } let codeUnitSequence = IteratorSequence( _NormalizedCodeUnitIterator(self[i..( // White-box testing: assume that the other N-bit to N-bit mixing functions // just dispatch to these. (Avalanche test is relatively expensive.) -HashingTestSuite.test("_Hasher.append(UInt64)/avalanche") { +HashingTestSuite.test("_Hasher.combine(UInt64)/avalanche") { avalancheTest(for: UInt64.self, _hashValue(for:), 0.02) } -HashingTestSuite.test("_Hasher.append(UInt32)/avalanche") { +HashingTestSuite.test("_Hasher.combine(UInt32)/avalanche") { avalancheTest(for: UInt32.self, _hashValue(for:), 0.02) } diff --git a/validation-test/stdlib/SipHash.swift b/validation-test/stdlib/SipHash.swift index 7bda072d069cf..cedd54c272d04 100644 --- a/validation-test/stdlib/SipHash.swift +++ b/validation-test/stdlib/SipHash.swift @@ -330,7 +330,7 @@ SipHashTests.test("${Self}.append(${data_type})").forEach(in: ${tests}) { var startIndex = 0 let endIndex = test.input.count - (test.input.count % chunkSize) while startIndex != endIndex { - hasher.append( + hasher.combine( loadUnaligned${data_type}( from: Array( test.input[startIndex..<(startIndex+chunkSize)]))) From 7e66a38af849ab26cb8a271b0d415fceb05bb408 Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Sat, 14 Apr 2018 17:18:35 +0100 Subject: [PATCH 2/3] [stdlib] De-gyb SipHash; implement full Hasher API --- stdlib/public/core/CMakeLists.txt | 3 +- stdlib/public/core/GroupInfo.json | 1 + stdlib/public/core/Hashable.swift | 2 + stdlib/public/core/Hasher.swift | 348 ++++++++++++++++++++ stdlib/public/core/Hashing.swift | 132 +------- stdlib/public/core/Integers.swift.gyb | 6 +- stdlib/public/core/SipHash.swift | 163 +++++++++ stdlib/public/core/SipHash.swift.gyb | 201 ----------- validation-test/stdlib/FixedPoint.swift.gyb | 6 +- validation-test/stdlib/Hashing.swift | 6 +- validation-test/stdlib/SipHash.swift | 222 ++++++------- 11 files changed, 635 insertions(+), 455 deletions(-) create mode 100644 stdlib/public/core/Hasher.swift create mode 100644 stdlib/public/core/SipHash.swift delete mode 100644 stdlib/public/core/SipHash.swift.gyb diff --git a/stdlib/public/core/CMakeLists.txt b/stdlib/public/core/CMakeLists.txt index 8f116da7fb187..5b21edda7f0e9 100644 --- a/stdlib/public/core/CMakeLists.txt +++ b/stdlib/public/core/CMakeLists.txt @@ -66,6 +66,7 @@ set(SWIFTLIB_ESSENTIAL AnyHashable.swift # END WORKAROUND HashedCollectionsAnyHashableExtensions.swift + Hasher.swift Hashing.swift HeapBuffer.swift ICU.swift @@ -105,7 +106,7 @@ set(SWIFTLIB_ESSENTIAL Reverse.swift Runtime.swift.gyb RuntimeFunctionCounters.swift - SipHash.swift.gyb + SipHash.swift SentinelCollection.swift Sequence.swift SequenceAlgorithms.swift diff --git a/stdlib/public/core/GroupInfo.json b/stdlib/public/core/GroupInfo.json index a1da8d9e74ca2..fd559016a2a71 100644 --- a/stdlib/public/core/GroupInfo.json +++ b/stdlib/public/core/GroupInfo.json @@ -165,6 +165,7 @@ "Interval.swift", "Hashing.swift", "SipHash.swift", + "Hasher.swift", "ErrorType.swift", "InputStream.swift", "LifetimeManager.swift", diff --git a/stdlib/public/core/Hashable.swift b/stdlib/public/core/Hashable.swift index 9a6312b641565..1eefe0f007671 100644 --- a/stdlib/public/core/Hashable.swift +++ b/stdlib/public/core/Hashable.swift @@ -114,6 +114,7 @@ public protocol Hashable : Equatable { } extension Hashable { + @inlinable @inline(__always) public func _hash(into hasher: inout _Hasher) { hasher.combine(self.hashValue) @@ -121,6 +122,7 @@ extension Hashable { } // Called by synthesized `hashValue` implementations. +@inlinable @inline(__always) public func _hashValue(for value: H) -> Int { var hasher = _Hasher() diff --git a/stdlib/public/core/Hasher.swift b/stdlib/public/core/Hasher.swift new file mode 100644 index 0000000000000..bbe85fb29219d --- /dev/null +++ b/stdlib/public/core/Hasher.swift @@ -0,0 +1,348 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Defines the Hasher struct, representing Swift's standard hash function. +// +//===----------------------------------------------------------------------===// + +import SwiftShims + +internal protocol _HasherCore { + init(seed: (UInt64, UInt64)) + mutating func compress(_ value: UInt64) + mutating func finalize(tailAndByteCount: UInt64) -> UInt64 +} + +@inline(__always) +internal func _loadPartialUnalignedUInt64LE( + _ p: UnsafeRawPointer, + byteCount: Int +) -> UInt64 { + var result: UInt64 = 0 + switch byteCount { + case 7: + result |= UInt64(p.load(fromByteOffset: 6, as: UInt8.self)) &<< 48 + fallthrough + case 6: + result |= UInt64(p.load(fromByteOffset: 5, as: UInt8.self)) &<< 40 + fallthrough + case 5: + result |= UInt64(p.load(fromByteOffset: 4, as: UInt8.self)) &<< 32 + fallthrough + case 4: + result |= UInt64(p.load(fromByteOffset: 3, as: UInt8.self)) &<< 24 + fallthrough + case 3: + result |= UInt64(p.load(fromByteOffset: 2, as: UInt8.self)) &<< 16 + fallthrough + case 2: + result |= UInt64(p.load(fromByteOffset: 1, as: UInt8.self)) &<< 8 + fallthrough + case 1: + result |= UInt64(p.load(fromByteOffset: 0, as: UInt8.self)) + fallthrough + case 0: + return result + default: + _sanityCheckFailure() + } +} + +/// This is a buffer for segmenting arbitrary data into 8-byte chunks. Buffer +/// storage is represented by a single 64-bit value in the format used by the +/// finalization step of SipHash. (The least significant 56 bits hold the +/// trailing bytes, while the most significant 8 bits hold the count of bytes +/// appended so far, modulo 256. The count of bytes currently stored in the +/// buffer is in the lower three bits of the byte count.) +internal struct _HasherTailBuffer { + // msb lsb + // +---------+-------+-------+-------+-------+-------+-------+-------+ + // |byteCount| tail (<= 56 bits) | + // +---------+-------+-------+-------+-------+-------+-------+-------+ + internal var value: UInt64 + + @inline(__always) + internal init() { + self.value = 0 + } + + @inline(__always) + internal init(tail: UInt64, byteCount: UInt64) { + // byteCount can be any value, but we only keep the lower 8 bits. (The + // lower three bits specify the count of bytes stored in this buffer.) + _sanityCheck(tail & ~(1 << ((byteCount & 7) << 3) - 1) == 0) + self.value = (byteCount &<< 56 | tail) + } + + internal var tail: UInt64 { + @inline(__always) + get { return value & ~(0xFF &<< 56) } + } + + internal var byteCount: UInt64 { + @inline(__always) + get { return value &>> 56 } + } + + internal var isFinalized: Bool { + @inline(__always) + get { return value == 1 } + } + + @inline(__always) + internal mutating func finalize() { + // A byteCount of 0 with a nonzero tail never occurs during normal use. + value = 1 + } + + @inline(__always) + internal mutating func append(_ bytes: UInt64) -> UInt64 { + let c = byteCount & 7 + if c == 0 { + value = value &+ (8 &<< 56) + return bytes + } + let shift = c &<< 3 + let chunk = tail | (bytes &<< shift) + value = (((value &>> 56) &+ 8) &<< 56) | (bytes &>> (64 - shift)) + return chunk + } + + @inline(__always) + internal + mutating func append(_ bytes: UInt64, count: UInt64) -> UInt64? { + _sanityCheck(count >= 0 && count < 8) + _sanityCheck(bytes & ~((1 &<< (count &<< 3)) &- 1) == 0) + let c = byteCount & 7 + let shift = c &<< 3 + if c + count < 8 { + value = (value | (bytes &<< shift)) &+ (count &<< 56) + return nil + } + let chunk = tail | (bytes &<< shift) + value = ((value &>> 56) &+ count) &<< 56 + if c + count > 8 { + value |= bytes &>> (64 - shift) + } + return chunk + } +} + +internal struct _BufferingHasher { + private var _buffer: _HasherTailBuffer + private var _core: Core + + @inline(__always) + internal init(seed: (UInt64, UInt64)) { + self._buffer = _HasherTailBuffer() + self._core = Core(seed: seed) + } + + @inline(__always) + internal mutating func combine(_ value: UInt) { +#if arch(i386) || arch(arm) + combine(UInt32(truncatingIfNeeded: value)) +#else + combine(UInt64(truncatingIfNeeded: value)) +#endif + } + + @inline(__always) + internal mutating func combine(_ value: UInt64) { + precondition(!_buffer.isFinalized) + _core.compress(_buffer.append(value)) + } + + @inline(__always) + internal mutating func combine(_ value: UInt32) { + precondition(!_buffer.isFinalized) + let value = UInt64(truncatingIfNeeded: value) + if let chunk = _buffer.append(value, count: 4) { + _core.compress(chunk) + } + } + + @inline(__always) + internal mutating func combine(_ value: UInt16) { + precondition(!_buffer.isFinalized) + let value = UInt64(truncatingIfNeeded: value) + if let chunk = _buffer.append(value, count: 2) { + _core.compress(chunk) + } + } + + @inline(__always) + internal mutating func combine(_ value: UInt8) { + precondition(!_buffer.isFinalized) + let value = UInt64(truncatingIfNeeded: value) + if let chunk = _buffer.append(value, count: 1) { + _core.compress(chunk) + } + } + + @inline(__always) + internal mutating func combine(bytes: UInt64, count: Int) { + precondition(!_buffer.isFinalized) + _sanityCheck(count <= 8) + let count = UInt64(truncatingIfNeeded: count) + if let chunk = _buffer.append(bytes, count: count) { + _core.compress(chunk) + } + } + + @inline(__always) + internal mutating func combine(bytes: UnsafeRawBufferPointer) { + precondition(!_buffer.isFinalized) + var remaining = bytes.count + guard remaining > 0 else { return } + var data = bytes.baseAddress! + + // Load first unaligned partial word of data + do { + let start = UInt(bitPattern: data) + let end = _roundUp(start, toAlignment: MemoryLayout.alignment) + let c = min(remaining, Int(end - start)) + if c > 0 { + let chunk = _loadPartialUnalignedUInt64LE(data, byteCount: c) + combine(bytes: chunk, count: c) + data += c + remaining -= c + } + } + _sanityCheck( + remaining == 0 || + Int(bitPattern: data) & (MemoryLayout.alignment - 1) == 0) + + // Load as many aligned words as there are in the input buffer + while remaining >= MemoryLayout.size { + combine(UInt64(littleEndian: data.load(as: UInt64.self))) + data += MemoryLayout.size + remaining -= MemoryLayout.size + } + + // Load last partial word of data + _sanityCheck(remaining >= 0 && remaining < 8) + if remaining > 0 { + let chunk = _loadPartialUnalignedUInt64LE(data, byteCount: remaining) + combine(bytes: chunk, count: remaining) + } + } + + @inline(__always) + internal mutating func finalize() -> UInt64 { + precondition(!_buffer.isFinalized) + let hash = _core.finalize(tailAndByteCount: _buffer.value) + _buffer.finalize() + return hash + } +} + +@_fixed_layout // FIXME: Should be resilient (rdar://problem/38549901) +public struct _Hasher { + internal typealias Core = _BufferingHasher<_SipHash13Core> + + private var _core: Core + + @effects(releasenone) + public init() { + self._core = Core(seed: _Hasher._seed) + } + + @usableFromInline + @effects(releasenone) + internal init(_seed seed: (UInt64, UInt64)) { + self._core = Core(seed: seed) + } + + /// Indicates whether we're running in an environment where hashing needs to + /// be deterministic. If this is true, the hash seed is not random, and hash + /// tables do not apply per-instance perturbation that is not repeatable. + /// This is not recommended for production use, but it is useful in certain + /// test environments where randomization may lead to unwanted nondeterminism + /// of test results. + public // SPI + static var _isDeterministic: Bool { + @inlinable + @inline(__always) + get { + return _swift_stdlib_Hashing_parameters.deterministic; + } + } + + /// The 128-bit hash seed used to initialize the hasher state. Initialized + /// once during process startup. + public // SPI + static var _seed: (UInt64, UInt64) { + @inlinable + @inline(__always) + get { + // The seed itself is defined in C++ code so that it is initialized during + // static construction. Almost every Swift program uses hash tables, so + // initializing the seed during the startup seems to be the right + // trade-off. + return ( + _swift_stdlib_Hashing_parameters.seed0, + _swift_stdlib_Hashing_parameters.seed1) + } + } + + @inlinable + @inline(__always) + public mutating func combine(_ value: H) { + value._hash(into: &self) + } + + //FIXME: Convert to @usableFromInline internal once integers hash correctly. + @effects(releasenone) + public mutating func _combine(_ value: UInt) { + _core.combine(value) + } + + //FIXME: Convert to @usableFromInline internal once integers hash correctly. + @effects(releasenone) + public mutating func _combine(_ value: UInt64) { + _core.combine(value) + } + + //FIXME: Convert to @usableFromInline internal once integers hash correctly. + @effects(releasenone) + public mutating func _combine(_ value: UInt32) { + _core.combine(value) + } + + //FIXME: Convert to @usableFromInline internal once integers hash correctly. + @effects(releasenone) + public mutating func _combine(_ value: UInt16) { + _core.combine(value) + } + + //FIXME: Convert to @usableFromInline internal once integers hash correctly. + @effects(releasenone) + public mutating func _combine(_ value: UInt8) { + _core.combine(value) + } + + @effects(releasenone) + public mutating func _combine(bytes value: UInt64, count: Int) { + _core.combine(bytes: value, count: count) + } + + @effects(releasenone) + public mutating func combine(bytes: UnsafeRawBufferPointer) { + _core.combine(bytes: bytes) + } + + @effects(releasenone) + public mutating func finalize() -> Int { + return Int(truncatingIfNeeded: _core.finalize()) + } +} diff --git a/stdlib/public/core/Hashing.swift b/stdlib/public/core/Hashing.swift index 1bb8c43a1d033..34fa8a4c45107 100644 --- a/stdlib/public/core/Hashing.swift +++ b/stdlib/public/core/Hashing.swift @@ -21,8 +21,6 @@ // ensure that hash values differ between executions. // -import SwiftShims - @_frozen // FIXME(sil-serialize-all) public // @testable enum _HashingDetail { @@ -156,143 +154,39 @@ func _combineHashValues(_ firstValue: Int, _ secondValue: Int) -> Int { // FIXME(hasher): This hasher emulates Swift 4.1 hashValues. It is purely for // benchmarking; to be removed. -internal struct _LegacyHasher { +internal struct _LegacyHasherCore: _HasherCore { internal var _hash: Int @inline(__always) - internal init(key: (UInt64, UInt64) = (0, 0)) { // key is ignored + internal init(seed: (UInt64, UInt64) = (0, 0)) { // seed is ignored _hash = 0 } @inline(__always) - internal mutating func combine(_ value: Int) { + internal mutating func compress(_ value: UInt64) { + let value = (UInt64.bitWidth > Int.bitWidth + ? Int(truncatingIfNeeded: value ^ (value &>> 32)) + : Int(truncatingIfNeeded: value)) _hash = (_hash == 0 ? value : _combineHashValues(_hash, value)) } @inline(__always) - internal mutating func combine(_ value: UInt) { - combine(Int(bitPattern: value)) - } - - @inline(__always) - internal mutating func combine(_ value: UInt32) { - combine(Int(truncatingIfNeeded: value)) - } - - @inline(__always) - internal mutating func combine(_ value: UInt64) { - if UInt64.bitWidth > Int.bitWidth { - combine(Int(truncatingIfNeeded: value ^ (value &>> 32))) - } else { - combine(Int(truncatingIfNeeded: value)) + internal mutating func finalize(tailAndByteCount: UInt64) -> UInt64 { + let count = (tailAndByteCount &>> 56) & 7 + if count > 0 { + compress(tailAndByteCount & ((1 &<< (count &<< 3)) - 1)) } - } - - @inline(__always) - internal mutating func finalize() -> UInt64 { return UInt64( _truncatingBits: UInt(bitPattern: _mixInt(_hash))._lowWord) } -} - - -// NOT @_fixed_layout -@_fixed_layout // FIXME - remove - radar 38549901 -public struct _Hasher { - internal typealias Core = _SipHash13 - // NOT @usableFromInline - internal var _core: Core - - // NOT @inlinable - @effects(releasenone) - public init() { - self._core = Core(key: _Hasher._seed) - } - - // NOT @inlinable - @effects(releasenone) - public init(_seed seed: (UInt64, UInt64)) { - self._core = Core(key: seed) - } - - /// Indicates whether we're running in an environment where hashing needs to - /// be deterministic. If this is true, the hash seed is not random, and hash - /// tables do not apply per-instance perturbation that is not repeatable. - /// This is not recommended for production use, but it is useful in certain - /// test environments where randomization may lead to unwanted nondeterminism - /// of test results. - public // SPI - static var _isDeterministic: Bool { - @inlinable - @inline(__always) - get { - return _swift_stdlib_Hashing_parameters.deterministic; - } - } - - /// The 128-bit hash seed used to initialize the hasher state. Initialized - /// once during process startup. - public // SPI - static var _seed: (UInt64, UInt64) { - @inlinable - @inline(__always) - get { - // The seed itself is defined in C++ code so that it is initialized during - // static construction. Almost every Swift program uses hash tables, so - // initializing the seed during the startup seems to be the right - // trade-off. - return ( - _swift_stdlib_Hashing_parameters.seed0, - _swift_stdlib_Hashing_parameters.seed1) - } - } - - @inlinable @inline(__always) - public mutating func combine(_ value: H) { - value._hash(into: &self) - } - - // NOT @inlinable - @effects(releasenone) - public mutating func combine(bits: UInt) { - _core.combine(bits) - } - // NOT @inlinable - @effects(releasenone) - public mutating func combine(bits: UInt32) { - _core.combine(bits) - } - // NOT @inlinable - @effects(releasenone) - public mutating func combine(bits: UInt64) { - _core.combine(bits) - } - - // NOT @inlinable - @effects(releasenone) - public mutating func combine(bits: Int) { - _core.combine(UInt(bitPattern: bits)) - } - // NOT @inlinable - @effects(releasenone) - public mutating func combine(bits: Int32) { - _core.combine(UInt32(bitPattern: bits)) - } - // NOT @inlinable - @effects(releasenone) - public mutating func combine(bits: Int64) { - _core.combine(UInt64(bitPattern: bits)) - } - - // NOT @inlinable - @effects(releasenone) - public mutating func finalize() -> Int { - return Int(truncatingIfNeeded: _core.finalize()) + func _generateSeed() -> (UInt64, UInt64) { + return (0, 0) } } + /// This protocol is only used for compile-time checks that /// every buffer type implements all required operations. internal protocol _HashBuffer { diff --git a/stdlib/public/core/Integers.swift.gyb b/stdlib/public/core/Integers.swift.gyb index f0f4fd766c163..393cfe8b7fb47 100644 --- a/stdlib/public/core/Integers.swift.gyb +++ b/stdlib/public/core/Integers.swift.gyb @@ -3715,12 +3715,12 @@ extension ${Self} : Hashable { // this, we should introduce a custom AnyHashable box for integer values // that sign-extends values to 64 bits. % if bits <= word_bits: - hasher.combine(bits: _lowWord) + hasher._combine(_lowWord) % elif bits == 2 * word_bits: if let word = ${"" if signed else "U"}Int(exactly: self) { - hasher.combine(bits: word._lowWord) + hasher._combine(word._lowWord) } else { - hasher.combine(bits: UInt64(_value)) + hasher._combine(UInt64(_value)) } % else: fatalError("Unsupported integer width") diff --git a/stdlib/public/core/SipHash.swift b/stdlib/public/core/SipHash.swift new file mode 100644 index 0000000000000..3f549c2335838 --- /dev/null +++ b/stdlib/public/core/SipHash.swift @@ -0,0 +1,163 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +/// This file implements SipHash-2-4 and SipHash-1-3 +/// (https://131002.net/siphash/). +/// +/// This file is based on the reference C implementation, which was released +/// to public domain by: +/// +/// * Jean-Philippe Aumasson +/// * Daniel J. Bernstein +//===----------------------------------------------------------------------===// + +private struct _SipHashState { + // "somepseudorandomlygeneratedbytes" + fileprivate var v0: UInt64 = 0x736f6d6570736575 + fileprivate var v1: UInt64 = 0x646f72616e646f6d + fileprivate var v2: UInt64 = 0x6c7967656e657261 + fileprivate var v3: UInt64 = 0x7465646279746573 + + @inline(__always) + fileprivate init(seed: (UInt64, UInt64)) { + v3 ^= seed.1 + v2 ^= seed.0 + v1 ^= seed.1 + v0 ^= seed.0 + } + + @inline(__always) + fileprivate + static func _rotateLeft(_ x: UInt64, by amount: UInt64) -> UInt64 { + return (x &<< amount) | (x &>> (64 - amount)) + } + + @inline(__always) + fileprivate mutating func _round() { + v0 = v0 &+ v1 + v1 = _SipHashState._rotateLeft(v1, by: 13) + v1 ^= v0 + v0 = _SipHashState._rotateLeft(v0, by: 32) + v2 = v2 &+ v3 + v3 = _SipHashState._rotateLeft(v3, by: 16) + v3 ^= v2 + v0 = v0 &+ v3 + v3 = _SipHashState._rotateLeft(v3, by: 21) + v3 ^= v0 + v2 = v2 &+ v1 + v1 = _SipHashState._rotateLeft(v1, by: 17) + v1 ^= v2 + v2 = _SipHashState._rotateLeft(v2, by: 32) + } + + @inline(__always) + fileprivate func _extract() -> UInt64 { + return v0 ^ v1 ^ v2 ^ v3 + } +} + +internal struct _SipHash13Core: _HasherCore { + private var _state: _SipHashState + + @inline(__always) + internal init(seed: (UInt64, UInt64)) { + _state = _SipHashState(seed: seed) + } + + @inline(__always) + internal mutating func compress(_ m: UInt64) { + _state.v3 ^= m + _state._round() + _state.v0 ^= m + } + + @inline(__always) + internal mutating func finalize(tailAndByteCount: UInt64) -> UInt64 { + compress(tailAndByteCount) + _state.v2 ^= 0xff + for _ in 0..<3 { + _state._round() + } + return _state._extract() + } +} + +internal struct _SipHash24Core: _HasherCore { + private var _state: _SipHashState + + @inline(__always) + internal init(seed: (UInt64, UInt64)) { + _state = _SipHashState(seed: seed) + } + + @inline(__always) + internal mutating func compress(_ m: UInt64) { + _state.v3 ^= m + _state._round() + _state._round() + _state.v0 ^= m + } + + @inline(__always) + internal mutating func finalize(tailAndByteCount: UInt64) -> UInt64 { + compress(tailAndByteCount) + + _state.v2 ^= 0xff + for _ in 0..<4 { + _state._round() + } + return _state._extract() + } +} + +// FIXME: This type only exists to facilitate testing. +public // @testable +struct _SipHash13 { + internal typealias Core = _BufferingHasher<_SipHash13Core> + + internal var _core: Core + + public init(seed: (UInt64, UInt64)) { _core = Core(seed: seed) } + public mutating func _combine(_ v: UInt) { _core.combine(v) } + public mutating func _combine(_ v: UInt64) { _core.combine(v) } + public mutating func _combine(_ v: UInt32) { _core.combine(v) } + public mutating func _combine(_ v: UInt16) { _core.combine(v) } + public mutating func _combine(_ v: UInt8) { _core.combine(v) } + public mutating func combine(bytes v: UInt64, count: Int) { + _core.combine(bytes: v, count: count) + } + public mutating func combine(bytes: UnsafeRawBufferPointer) { + _core.combine(bytes: bytes) + } + public mutating func finalize() -> UInt64 { return _core.finalize() } +} + +// FIXME: This type only exists to facilitate testing. +public // @testable +struct _SipHash24 { + internal typealias Core = _BufferingHasher<_SipHash24Core> + + internal var _core: Core + + public init(seed: (UInt64, UInt64)) { _core = Core(seed: seed) } + public mutating func _combine(_ v: UInt) { _core.combine(v) } + public mutating func _combine(_ v: UInt64) { _core.combine(v) } + public mutating func _combine(_ v: UInt32) { _core.combine(v) } + public mutating func _combine(_ v: UInt16) { _core.combine(v) } + public mutating func _combine(_ v: UInt8) { _core.combine(v) } + public mutating func combine(bytes v: UInt64, count: Int) { + _core.combine(bytes: v, count: count) + } + public mutating func combine(bytes: UnsafeRawBufferPointer) { + _core.combine(bytes: bytes) + } + public mutating func finalize() -> UInt64 { return _core.finalize() } +} diff --git a/stdlib/public/core/SipHash.swift.gyb b/stdlib/public/core/SipHash.swift.gyb deleted file mode 100644 index eb4ba274696d9..0000000000000 --- a/stdlib/public/core/SipHash.swift.gyb +++ /dev/null @@ -1,201 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// -/// This file implements SipHash-2-4 and SipHash-1-3 -/// (https://131002.net/siphash/). -/// -/// This file is based on the reference C implementation, which was released -/// to public domain by: -/// -/// * Jean-Philippe Aumasson -/// * Daniel J. Bernstein -//===----------------------------------------------------------------------===// - -%{ -# Number of bits in the Builtin.Word type -word_bits = int(CMAKE_SIZEOF_VOID_P) * 8 -}% - -@_frozen // FIXME(sil-serialize-all) -@usableFromInline -internal enum _SipHashDetail { - @usableFromInline - @inline(__always) - internal static func _rotate(_ x: UInt64, leftBy amount: UInt64) -> UInt64 { - return (x &<< amount) | (x &>> (64 - amount)) - } - - @usableFromInline - @inline(__always) - internal static func _sipRound( - v0: inout UInt64, - v1: inout UInt64, - v2: inout UInt64, - v3: inout UInt64 - ) { - v0 = v0 &+ v1 - v1 = _rotate(v1, leftBy: 13) - v1 ^= v0 - v0 = _rotate(v0, leftBy: 32) - v2 = v2 &+ v3 - v3 = _rotate(v3, leftBy: 16) - v3 ^= v2 - v0 = v0 &+ v3 - v3 = _rotate(v3, leftBy: 21) - v3 ^= v0 - v2 = v2 &+ v1 - v1 = _rotate(v1, leftBy: 17) - v1 ^= v2 - v2 = _rotate(v2, leftBy: 32) - } -} - -% for (c_rounds, d_rounds) in [(2, 4), (1, 3)]: -% Self = '_SipHash{}{}'.format(c_rounds, d_rounds) - -@_fixed_layout // FIXME(sil-serialize-all) -public // @testable -struct ${Self} { - // "somepseudorandomlygeneratedbytes" - @usableFromInline - internal var v0: UInt64 = 0x736f6d6570736575 - - @usableFromInline - internal var v1: UInt64 = 0x646f72616e646f6d - - @usableFromInline - internal var v2: UInt64 = 0x6c7967656e657261 - - @usableFromInline - internal var v3: UInt64 = 0x7465646279746573 - - /// This value holds the byte count and the pending bytes that haven't been - /// compressed yet, in the format that the finalization step needs. (The least - /// significant 56 bits hold the trailing bytes, while the most significant 8 - /// bits hold the count of bytes combined so far, mod 256.) - @usableFromInline - internal var tailAndByteCount: UInt64 = 0 - - @inline(__always) - public init(key: (UInt64, UInt64)) { - v3 ^= key.1 - v2 ^= key.0 - v1 ^= key.1 - v0 ^= key.0 - } - - @usableFromInline - internal var byteCount: UInt64 { - @inline(__always) - get { - return tailAndByteCount &>> 56 - } - } - - @usableFromInline - internal var tail: UInt64 { - @inline(__always) - get { - return tailAndByteCount & ~(0xFF &<< 56) - } - } - - @inline(__always) - @usableFromInline - internal mutating func _compress(_ m: UInt64) { - v3 ^= m - for _ in 0..<${c_rounds} { - _SipHashDetail._sipRound(v0: &v0, v1: &v1, v2: &v2, v3: &v3) - } - v0 ^= m - } - - @inline(__always) - public mutating func combine(_ value: Int) { - combine(UInt(bitPattern: value)) - } - - @inline(__always) - public mutating func combine(_ value: UInt) { - % if word_bits == 64: - combine(UInt64(_truncatingBits: value._lowWord)) - % elif word_bits == 32: - combine(UInt32(_truncatingBits: value._lowWord)) - % else: - fatalError("Unsupported word width") - % end - } - - @inline(__always) - public mutating func combine(_ value: Int32) { - combine(UInt32(bitPattern: value)) - } - - @inline(__always) - public mutating func combine(_ value: UInt32) { - let m = UInt64(_truncatingBits: value._lowWord) - if byteCount & 4 == 0 { - _sanityCheck(byteCount & 7 == 0 && tail == 0) - tailAndByteCount = (tailAndByteCount | m) &+ (4 &<< 56) - } else { - _sanityCheck(byteCount & 3 == 0) - _compress((m &<< 32) | tail) - tailAndByteCount = (byteCount &+ 4) &<< 56 - } - } - - @inline(__always) - public mutating func combine(_ value: Int64) { - combine(UInt64(bitPattern: value)) - } - - @inline(__always) - public mutating func combine(_ m: UInt64) { - if byteCount & 4 == 0 { - _sanityCheck(byteCount & 7 == 0 && tail == 0) - _compress(m) - tailAndByteCount = tailAndByteCount &+ (8 &<< 56) - } else { - _sanityCheck(byteCount & 3 == 0) - _compress((m &<< 32) | tail) - tailAndByteCount = ((byteCount &+ 8) &<< 56) | (m &>> 32) - } - } - - @inline(__always) - public mutating func finalize( - tailBytes: UInt64, - tailByteCount: Int - ) -> UInt64 { - _sanityCheck(tailByteCount >= 0) - _sanityCheck(tailByteCount < 8 - (byteCount & 7)) - _sanityCheck(tailBytes >> (tailByteCount << 3) == 0) - let count = UInt64(_truncatingBits: tailByteCount._lowWord) - let currentByteCount = byteCount & 7 - tailAndByteCount |= (tailBytes &<< (currentByteCount &<< 3)) - tailAndByteCount = tailAndByteCount &+ (count &<< 56) - return finalize() - } - - @inline(__always) - public mutating func finalize() -> UInt64 { - _compress(tailAndByteCount) - - v2 ^= 0xff - - for _ in 0..<${d_rounds} { - _SipHashDetail._sipRound(v0: &v0, v1: &v1, v2: &v2, v3: &v3) - } - - return (v0 ^ v1 ^ v2 ^ v3) - } -} -% end diff --git a/validation-test/stdlib/FixedPoint.swift.gyb b/validation-test/stdlib/FixedPoint.swift.gyb index 976114d51b6a6..3426bb75c5af2 100644 --- a/validation-test/stdlib/FixedPoint.swift.gyb +++ b/validation-test/stdlib/FixedPoint.swift.gyb @@ -242,11 +242,11 @@ FixedPoint.test("${Self}.hashValue") { let input = get${Self}(${input}) let output = getInt(input.hashValue) - var hasher = _SipHash13(key: _Hasher._seed) + var hasher = _SipHash13(seed: _Hasher._seed) % if prepare_bit_pattern(input, word_bits, self_ty.is_signed) == input: - hasher.combine(${input} as ${"" if self_ty.is_signed else "U"}Int) + hasher._combine(UInt(truncatingIfNeeded: ${input} as ${"" if self_ty.is_signed else "U"}Int)) % else: - hasher.combine(input) + hasher._combine(input) % end let expected = getInt(Int(truncatingIfNeeded: hasher.finalize())) expectEqual(expected, output, "input: \(input)") diff --git a/validation-test/stdlib/Hashing.swift b/validation-test/stdlib/Hashing.swift index 3b741268ff674..60eb28f57b9fc 100644 --- a/validation-test/stdlib/Hashing.swift +++ b/validation-test/stdlib/Hashing.swift @@ -15,7 +15,7 @@ func checkHash( file: String = #file, line: UInt = #line ) { var hasher = _Hasher(_seed: seed) - hasher.combine(bits: value) + hasher._combine(value) let hash = hasher.finalize() expectEqual( hash, Int(truncatingIfNeeded: expected), @@ -49,11 +49,11 @@ HashingTestSuite.test("_Hasher/DefaultKey") { let defaultHash = _hashValue(for: value) var defaultHasher = _Hasher() - defaultHasher.combine(bits: value) + defaultHasher._combine(value) expectEqual(defaultHasher.finalize(), defaultHash) var customHasher = _Hasher(_seed: _Hasher._seed) - customHasher.combine(bits: value) + customHasher._combine(value) expectEqual(customHasher.finalize(), defaultHash) } diff --git a/validation-test/stdlib/SipHash.swift b/validation-test/stdlib/SipHash.swift index cedd54c272d04..fa76a497e12d7 100644 --- a/validation-test/stdlib/SipHash.swift +++ b/validation-test/stdlib/SipHash.swift @@ -7,14 +7,14 @@ let SipHashTests = TestSuite("SipHashTests") struct SipHashTest { let input: [UInt8] - let key: (UInt64, UInt64) + let seed: (UInt64, UInt64) let output: UInt64 /// Test vector from the reference C implementation. /// /// SipHash output with /// - /// key = 00 01 02 ... + /// seed = 00 01 02 ... /// /// and /// @@ -26,13 +26,13 @@ struct SipHashTest { /// input = 00 01 02 ... 3e (63 bytes) init(referenceVectorIndex i: Int, output: UInt64) { self.input = Array(0.. UInt64 { - return - UInt64(p.load(fromByteOffset: 0, as: UInt8.self)) | - (UInt64(p.load(fromByteOffset: 1, as: UInt8.self)) &<< 8 as UInt64) | - (UInt64(p.load(fromByteOffset: 2, as: UInt8.self)) &<< 16 as UInt64) | - (UInt64(p.load(fromByteOffset: 3, as: UInt8.self)) &<< 24 as UInt64) | - (UInt64(p.load(fromByteOffset: 4, as: UInt8.self)) &<< 32 as UInt64) | - (UInt64(p.load(fromByteOffset: 5, as: UInt8.self)) &<< 40 as UInt64) | - (UInt64(p.load(fromByteOffset: 6, as: UInt8.self)) &<< 48 as UInt64) | - (UInt64(p.load(fromByteOffset: 7, as: UInt8.self)) &<< 56 as UInt64) -} +struct Loop: Sequence, IteratorProtocol { + var base: C + var iterator: C.Iterator -func loadUnalignedUInt32LE( - from p: UnsafeRawPointer -) -> UInt32 { - return - UInt32(p.load(fromByteOffset: 0, as: UInt8.self)) | - (UInt32(p.load(fromByteOffset: 1, as: UInt8.self)) &<< 8 as UInt32) | - (UInt32(p.load(fromByteOffset: 2, as: UInt8.self)) &<< 16 as UInt32) | - (UInt32(p.load(fromByteOffset: 3, as: UInt8.self)) &<< 24 as UInt32) -} + init(_ base: C) { + self.base = base + self.iterator = base.makeIterator() + } -func loadUnalignedUIntLE( - from p: UnsafeRawPointer -) -> UInt { -#if arch(i386) || arch(arm) - return UInt(loadUnalignedUInt32LE(from: p)) -#elseif arch(x86_64) || arch(arm64) || arch(powerpc64) || arch(powerpc64le) || arch(s390x) - return UInt(loadUnalignedUInt64LE(from: p)) -#endif + mutating func next() -> C.Element? { + if let element = iterator.next() { + return element + } + iterator = base.makeIterator() + return iterator.next() + } } -func loadPartialUnalignedUInt64LE( - from p: UnsafeRawPointer, - byteCount: Int -) -> UInt64 { - _sanityCheck((0..<8).contains(byteCount)) - var result: UInt64 = 0 - if byteCount >= 1 { result |= UInt64(p.load(fromByteOffset: 0, as: UInt8.self)) } - if byteCount >= 2 { result |= UInt64(p.load(fromByteOffset: 1, as: UInt8.self)) &<< (8 as UInt64) } - if byteCount >= 3 { result |= UInt64(p.load(fromByteOffset: 2, as: UInt8.self)) &<< (16 as UInt64) } - if byteCount >= 4 { result |= UInt64(p.load(fromByteOffset: 3, as: UInt8.self)) &<< (24 as UInt64) } - if byteCount >= 5 { result |= UInt64(p.load(fromByteOffset: 4, as: UInt8.self)) &<< (32 as UInt64) } - if byteCount >= 6 { result |= UInt64(p.load(fromByteOffset: 5, as: UInt8.self)) &<< (40 as UInt64) } - if byteCount >= 7 { result |= UInt64(p.load(fromByteOffset: 6, as: UInt8.self)) &<< (48 as UInt64) } - return result -} +% for (Self, tests) in [ +% ('_SipHash13', 'sipHash13Tests'), +% ('_SipHash24', 'sipHash24Tests') +% ]: -func loadPartialUnalignedUInt32LE( - from p: UnsafeRawPointer, - byteCount: Int -) -> UInt32 { - _sanityCheck((0..<4).contains(byteCount)) - var result: UInt32 = 0 - if byteCount >= 1 { result |= UInt32(p.load(fromByteOffset: 0, as: UInt8.self)) } - if byteCount >= 2 { result |= UInt32(p.load(fromByteOffset: 1, as: UInt8.self)) &<< (8 as UInt32) } - if byteCount >= 3 { result |= UInt32(p.load(fromByteOffset: 2, as: UInt8.self)) &<< (16 as UInt32) } - return result +SipHashTests.test("${Self}/combine(UnsafeRawBufferPointer)") + .forEach(in: ${tests}) { test in + var hasher = ${Self}(seed: test.seed) + test.input.withUnsafeBytes { hasher.combine(bytes: $0) } + let hash = hasher.finalize() + expectEqual(test.output, hash) } -func loadPartialUnalignedUIntLE( - from p: UnsafeRawPointer, - byteCount: Int -) -> UInt { -#if arch(i386) || arch(arm) - return UInt(loadPartialUnalignedUInt32LE(from: p, byteCount: byteCount)) -#elseif arch(x86_64) || arch(arm64) || arch(powerpc64) || arch(powerpc64le) || arch(s390x) - return UInt(loadPartialUnalignedUInt64LE(from: p, byteCount: byteCount)) -#endif -} +SipHashTests.test("${Self}/combine(UnsafeRawBufferPointer)/pattern") + .forEach(in: cartesianProduct(${tests}, incrementalPatterns)) { test_ in + let (test, pattern) = test_ -% for data_type in ['Int', 'Int64', 'Int32']: -func loadUnaligned${data_type}LE( - from p: UnsafeRawPointer -) -> ${data_type} { - return ${data_type}(bitPattern: loadUnalignedU${data_type}LE(from: p)) + var hasher = ${Self}(seed: test.seed) + var chunkSizes = Loop(pattern).makeIterator() + var startIndex = 0 + while startIndex != test.input.endIndex { + let chunkSize = min( + chunkSizes.next()!, + test.input.endIndex - startIndex) + let slice = test.input[startIndex..<(startIndex+chunkSize)] + slice.withUnsafeBytes { hasher.combine(bytes: $0) } + startIndex += chunkSize + } + expectEqual(test.output, hasher.finalize()) } -func loadPartialUnaligned${data_type}LE( - from p: UnsafeRawPointer, - byteCount: Int -) -> ${data_type} { - return ${data_type}( - bitPattern: loadPartialUnalignedU${data_type}LE( - from: p, - byteCount: byteCount)) -} -% end +% for data_type in ['UInt', 'UInt64', 'UInt32', 'UInt16', 'UInt8']: +SipHashTests.test("${Self}._combine(${data_type})") + .forEach(in: ${tests}) { test in + + var hasher = ${Self}(seed: test.seed) -% for data_type in ['UInt', 'Int', 'UInt64', 'Int64', 'UInt32', 'Int32']: -func loadUnaligned${data_type}( - from p: UnsafeRawPointer -) -> ${data_type} { - return ${data_type}(littleEndian: loadUnaligned${data_type}LE(from: p)) + // Load little-endian chunks and combine them into the hasher. + let bitWidth = ${data_type}.bitWidth + var i = 0 + var count = 0 + var chunk: ${data_type} = 0 + while i < test.input.count { + chunk |= ${data_type}(test.input[i]) << (8 * count) + i += 1 + count += 1 + if 8 * count == bitWidth { + hasher._combine(chunk) + count = 0 + chunk = 0 + } + } + // Combine tail bytes. + if count > 0 { + hasher.combine(bytes: UInt64(truncatingIfNeeded: chunk), count: count) + } + + let hash = hasher.finalize() + expectEqual(test.output, hash) } -func loadPartialUnaligned${data_type}( - from p: UnsafeRawPointer, - byteCount: Int -) -> ${data_type} { - return ${data_type}(littleEndian: - loadPartialUnaligned${data_type}LE(from: p, byteCount: byteCount)) + +SipHashTests.test("${Self}/CombineAfterFinalize/${data_type}") { + var hasher = ${Self}(seed: (0, 0)) + _ = hasher.finalize() + expectCrashLater() + hasher._combine(42 as ${data_type}) } % end -% for (Self, tests) in [ -% ('_SipHash13', 'sipHash13Tests'), -% ('_SipHash24', 'sipHash24Tests') -% ]: -% for data_type in ['UInt', 'Int', 'UInt64', 'Int64', 'UInt32', 'Int32']: -SipHashTests.test("${Self}.append(${data_type})").forEach(in: ${tests}) { - test in - - var hasher = ${Self}(key: test.key) +SipHashTests.test("${Self}/CombineAfterFinalize/PartialUInt64") { + var hasher = ${Self}(seed: (0, 0)) + _ = hasher.finalize() + expectCrashLater() + hasher.combine(bytes: 42, count: 1) +} - let chunkSize = MemoryLayout<${data_type}>.size +SipHashTests.test("${Self}/CombineAfterFinalize/UnsafeRawBufferPointer") { + var hasher = ${Self}(seed: (0, 0)) + _ = hasher.finalize() + expectCrashLater() + var v = 42 + withUnsafeBytes(of: &v) { hasher.combine(bytes: $0) } +} - var startIndex = 0 - let endIndex = test.input.count - (test.input.count % chunkSize) - while startIndex != endIndex { - hasher.combine( - loadUnaligned${data_type}( - from: Array( - test.input[startIndex..<(startIndex+chunkSize)]))) - startIndex += chunkSize - } - let tailCount = test.input.count - endIndex - let hash = hasher.finalize( - tailBytes: loadPartialUnalignedUInt64( - from: Array(test.input.suffix(from: endIndex)), - byteCount: tailCount), - tailByteCount: tailCount) - expectEqual(test.output, hash) +SipHashTests.test("${Self}/FinalizeAfterFinalize") { + var hasher = ${Self}(seed: (0, 0)) + _ = hasher.finalize() + expectCrashLater() + _ = hasher.finalize() } % end -% end runAllTests() From dffbbd1cda3ba0b20e955e2a2a1224c7859b9be4 Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Wed, 18 Apr 2018 20:19:59 +0100 Subject: [PATCH 3/3] [test] FixedPoint: Fix 32-bit regression --- validation-test/stdlib/FixedPoint.swift.gyb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validation-test/stdlib/FixedPoint.swift.gyb b/validation-test/stdlib/FixedPoint.swift.gyb index 3426bb75c5af2..531709aa775bd 100644 --- a/validation-test/stdlib/FixedPoint.swift.gyb +++ b/validation-test/stdlib/FixedPoint.swift.gyb @@ -246,7 +246,7 @@ FixedPoint.test("${Self}.hashValue") { % if prepare_bit_pattern(input, word_bits, self_ty.is_signed) == input: hasher._combine(UInt(truncatingIfNeeded: ${input} as ${"" if self_ty.is_signed else "U"}Int)) % else: - hasher._combine(input) + hasher._combine(UInt64(truncatingIfNeeded: input)) % end let expected = getInt(Int(truncatingIfNeeded: hasher.finalize())) expectEqual(expected, output, "input: \(input)")