Skip to content

Commit 87685aa

Browse files
committed
[SE-0206][stdlib] Make Hasher.finalize() nonmutating but __consuming
As noted in the proposal’s revision, this allows us to get rid of finalization checks, improves API robustness, and paves the way for making Hasher move-only in the future.
1 parent de66338 commit 87685aa

File tree

4 files changed

+19
-31
lines changed

4 files changed

+19
-31
lines changed

Diff for: stdlib/public/core/Dictionary.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -1461,7 +1461,7 @@ extension Dictionary: Hashable where Value: Hashable {
14611461
var elementHasher = Hasher()
14621462
elementHasher.combine(k)
14631463
elementHasher.combine(v)
1464-
commutativeHash ^= elementHasher.finalize()
1464+
commutativeHash ^= elementHasher._finalize()
14651465
}
14661466
hasher.combine(commutativeHash)
14671467
}

Diff for: stdlib/public/core/Hashable.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ extension Hashable {
127127
public func _hashValue<H: Hashable>(for value: H) -> Int {
128128
var hasher = Hasher()
129129
hasher.combine(value)
130-
return hasher.finalize()
130+
return hasher._finalize()
131131
}
132132

133133
// Called by the SwiftValue implementation.

Diff for: stdlib/public/core/Hasher.swift

+16-28
Original file line numberDiff line numberDiff line change
@@ -93,17 +93,6 @@ internal struct _HasherTailBuffer {
9393
get { return value &>> 56 }
9494
}
9595

96-
internal var isFinalized: Bool {
97-
@inline(__always)
98-
get { return value == 1 }
99-
}
100-
101-
@inline(__always)
102-
internal mutating func finalize() {
103-
// A byteCount of 0 with a nonzero tail never occurs during normal use.
104-
value = 1
105-
}
106-
10796
@inline(__always)
10897
internal mutating func append(_ bytes: UInt64) -> UInt64 {
10998
let c = byteCount & 7
@@ -158,13 +147,11 @@ internal struct _BufferingHasher<Core: _HasherCore> {
158147

159148
@inline(__always)
160149
internal mutating func combine(_ value: UInt64) {
161-
precondition(!_buffer.isFinalized)
162150
_core.compress(_buffer.append(value))
163151
}
164152

165153
@inline(__always)
166154
internal mutating func combine(_ value: UInt32) {
167-
precondition(!_buffer.isFinalized)
168155
let value = UInt64(truncatingIfNeeded: value)
169156
if let chunk = _buffer.append(value, count: 4) {
170157
_core.compress(chunk)
@@ -173,7 +160,6 @@ internal struct _BufferingHasher<Core: _HasherCore> {
173160

174161
@inline(__always)
175162
internal mutating func combine(_ value: UInt16) {
176-
precondition(!_buffer.isFinalized)
177163
let value = UInt64(truncatingIfNeeded: value)
178164
if let chunk = _buffer.append(value, count: 2) {
179165
_core.compress(chunk)
@@ -182,7 +168,6 @@ internal struct _BufferingHasher<Core: _HasherCore> {
182168

183169
@inline(__always)
184170
internal mutating func combine(_ value: UInt8) {
185-
precondition(!_buffer.isFinalized)
186171
let value = UInt64(truncatingIfNeeded: value)
187172
if let chunk = _buffer.append(value, count: 1) {
188173
_core.compress(chunk)
@@ -191,7 +176,6 @@ internal struct _BufferingHasher<Core: _HasherCore> {
191176

192177
@inline(__always)
193178
internal mutating func combine(bytes: UInt64, count: Int) {
194-
precondition(!_buffer.isFinalized)
195179
_sanityCheck(count <= 8)
196180
let count = UInt64(truncatingIfNeeded: count)
197181
if let chunk = _buffer.append(bytes, count: count) {
@@ -201,7 +185,6 @@ internal struct _BufferingHasher<Core: _HasherCore> {
201185

202186
@inline(__always)
203187
internal mutating func combine(bytes: UnsafeRawBufferPointer) {
204-
precondition(!_buffer.isFinalized)
205188
var remaining = bytes.count
206189
guard remaining > 0 else { return }
207190
var data = bytes.baseAddress!
@@ -239,10 +222,7 @@ internal struct _BufferingHasher<Core: _HasherCore> {
239222

240223
@inline(__always)
241224
internal mutating func finalize() -> UInt64 {
242-
precondition(!_buffer.isFinalized)
243-
let hash = _core.finalize(tailAndByteCount: _buffer.value)
244-
_buffer.finalize()
245-
return hash
225+
return _core.finalize(tailAndByteCount: _buffer.value)
246226
}
247227
}
248228

@@ -258,12 +238,11 @@ internal struct _BufferingHasher<Core: _HasherCore> {
258238
/// hasher.combine("Hello")
259239
/// let hashValue = hasher.finalize()
260240
///
261-
/// Within the execution of a Swift program, `Hasher` guarantees that
262-
/// `finalize()` will always return the same value as long as it is fed the
263-
/// exact same sequence of bytes. However, the underlying hash algorithm is
264-
/// designed to exhibit avalanche effects: slight changes to the seed or the
265-
/// input byte sequence will typically produce drastic changes in the generated
266-
/// hash value.
241+
/// Within the execution of a Swift program, `Hasher` guarantees that finalizing
242+
/// it will always produce the same hash value as long as it is fed the exact
243+
/// same sequence of bytes. However, the underlying hash algorithm is designed
244+
/// to exhibit avalanche effects: slight changes to the seed or the input byte
245+
/// sequence will typically produce drastic changes in the generated hash value.
267246
///
268247
/// - Note: Do not save or otherwise reuse hash values across executions of your
269248
/// program. `Hasher` is usually randomly seeded, which means it will return
@@ -377,7 +356,16 @@ public struct Hasher {
377356
/// Finalizing invalidates the hasher; additional bits cannot be combined
378357
/// into it, and it cannot be finalized again.
379358
@effects(releasenone)
380-
public mutating func finalize() -> Int {
359+
@usableFromInline
360+
internal mutating func _finalize() -> Int {
381361
return Int(truncatingIfNeeded: _core.finalize())
382362
}
363+
364+
/// Finalize the hasher state and return the hash value. Finalizing consumes
365+
/// the hasher, forbidding further operations.
366+
@effects(releasenone)
367+
public __consuming func finalize() -> Int {
368+
var core = _core
369+
return Int(truncatingIfNeeded: core.finalize())
370+
}
383371
}

Diff for: stdlib/public/core/Set.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -2051,7 +2051,7 @@ extension _NativeSetBuffer where Element: Hashable
20512051
internal func _bucket(_ k: Key) -> Int {
20522052
var hasher = Hasher(_seed: _storage.seed)
20532053
hasher.combine(k)
2054-
return hasher.finalize() & _bucketMask
2054+
return hasher._finalize() & _bucketMask
20552055
}
20562056

20572057
@inlinable // FIXME(sil-serialize-all)

0 commit comments

Comments
 (0)