Skip to content

Commit dce36dc

Browse files
authored
Merge pull request #20202 from lorentey/hasher-padding-redux3
[stdlib] Add 256 bits of extra state to Hasher
2 parents 2dd8e8b + 3ae170c commit dce36dc

File tree

5 files changed

+141
-321
lines changed

5 files changed

+141
-321
lines changed

stdlib/public/core/Hasher.swift

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -162,24 +162,24 @@ internal struct _HasherTailBuffer {
162162
// FIXME: Remove @usableFromInline and @_fixed_layout once Hasher is resilient.
163163
// rdar://problem/38549901
164164
@usableFromInline @_fixed_layout
165-
internal struct _BufferingHasher<RawCore: _HasherCore> {
165+
internal struct _BufferingHasher<Core: _HasherCore> {
166166
private var _buffer: _HasherTailBuffer
167-
private var _core: RawCore
167+
private var _core: Core
168168

169169
@inline(__always)
170-
internal init(core: RawCore) {
170+
internal init(core: Core) {
171171
self._buffer = _HasherTailBuffer()
172172
self._core = core
173173
}
174174

175175
@inline(__always)
176176
internal init() {
177-
self.init(core: RawCore())
177+
self.init(core: Core())
178178
}
179179

180180
@inline(__always)
181181
internal init(seed: Int) {
182-
self.init(core: RawCore(seed: seed))
182+
self.init(core: Core(seed: seed))
183183
}
184184

185185
@inline(__always)
@@ -300,36 +300,32 @@ public struct Hasher {
300300
// FIXME: Remove @usableFromInline once Hasher is resilient.
301301
// rdar://problem/38549901
302302
@usableFromInline
303-
internal typealias RawCore = _SipHash13Core
304-
// FIXME: Remove @usableFromInline once Hasher is resilient.
305-
// rdar://problem/38549901
306-
@usableFromInline
307-
internal typealias Core = _BufferingHasher<RawCore>
303+
internal typealias _BufferingCore = _BufferingHasher<_Core>
308304

309-
internal var _core: Core
305+
internal var _core: _BufferingCore
310306

311307
/// Creates a new hasher.
312308
///
313309
/// The hasher uses a per-execution seed value that is set during process
314310
/// startup, usually from a high-quality random source.
315311
@_effects(releasenone)
316312
public init() {
317-
self._core = Core()
313+
self._core = _BufferingCore()
318314
}
319315

320316
/// Initialize a new hasher using the specified seed value.
321317
/// The provided seed is mixed in with the global execution seed.
322318
@usableFromInline
323319
@_effects(releasenone)
324320
internal init(_seed: Int) {
325-
self._core = Core(seed: _seed)
321+
self._core = _BufferingCore(seed: _seed)
326322
}
327323

328324
/// Initialize a new hasher using the specified seed value.
329325
@usableFromInline // @testable
330326
@_effects(releasenone)
331327
internal init(_rawSeed: (UInt64, UInt64)) {
332-
self._core = Core(core: RawCore(rawSeed: _rawSeed))
328+
self._core = _BufferingCore(core: _Core(rawSeed: _rawSeed))
333329
}
334330

335331
/// Indicates whether we're running in an environment where hashing needs to
@@ -445,7 +441,7 @@ public struct Hasher {
445441
@_effects(readnone)
446442
@usableFromInline
447443
internal static func _hash(seed: Int, _ value: UInt64) -> Int {
448-
var core = RawCore(seed: seed)
444+
var core = _Core(seed: seed)
449445
core.compress(value)
450446
let tbc = _HasherTailBuffer(tail: 0, byteCount: 8)
451447
return Int(truncatingIfNeeded: core.finalize(tailAndByteCount: tbc.value))
@@ -454,7 +450,7 @@ public struct Hasher {
454450
@_effects(readnone)
455451
@usableFromInline
456452
internal static func _hash(seed: Int, _ value: UInt) -> Int {
457-
var core = RawCore(seed: seed)
453+
var core = _Core(seed: seed)
458454
#if arch(i386) || arch(arm)
459455
_sanityCheck(UInt.bitWidth < UInt64.bitWidth)
460456
let tbc = _HasherTailBuffer(
@@ -475,7 +471,7 @@ public struct Hasher {
475471
bytes value: UInt64,
476472
count: Int) -> Int {
477473
_sanityCheck(count >= 0 && count < 8)
478-
var core = RawCore(seed: seed)
474+
var core = _Core(seed: seed)
479475
let tbc = _HasherTailBuffer(tail: value, byteCount: count)
480476
return Int(truncatingIfNeeded: core.finalize(tailAndByteCount: tbc.value))
481477
}
@@ -485,7 +481,7 @@ public struct Hasher {
485481
internal static func _hash(
486482
seed: Int,
487483
bytes: UnsafeRawBufferPointer) -> Int {
488-
var core = Core(seed: seed)
484+
var core = _BufferingCore(seed: seed)
489485
core.combine(bytes: bytes)
490486
return Int(truncatingIfNeeded: core.finalize())
491487
}

stdlib/public/core/SipHash.swift

Lines changed: 71 additions & 164 deletions
Original file line numberDiff line numberDiff line change
@@ -19,181 +19,88 @@
1919
/// * Daniel J. Bernstein <[email protected]>
2020
//===----------------------------------------------------------------------===//
2121

22-
// FIXME: Remove @usableFromInline and @_fixed_layout once Hasher is resilient.
23-
// rdar://problem/38549901
24-
@usableFromInline @_fixed_layout
25-
internal struct _SipHashState {
26-
// "somepseudorandomlygeneratedbytes"
27-
fileprivate var v0: UInt64 = 0x736f6d6570736575
28-
fileprivate var v1: UInt64 = 0x646f72616e646f6d
29-
fileprivate var v2: UInt64 = 0x6c7967656e657261
30-
fileprivate var v3: UInt64 = 0x7465646279746573
31-
32-
@inline(__always)
33-
fileprivate init(rawSeed: (UInt64, UInt64)) {
34-
v3 ^= rawSeed.1
35-
v2 ^= rawSeed.0
36-
v1 ^= rawSeed.1
37-
v0 ^= rawSeed.0
38-
}
39-
40-
@inline(__always)
41-
fileprivate
42-
static func _rotateLeft(_ x: UInt64, by amount: UInt64) -> UInt64 {
43-
return (x &<< amount) | (x &>> (64 - amount))
44-
}
45-
46-
@inline(__always)
47-
fileprivate mutating func _round() {
48-
v0 = v0 &+ v1
49-
v1 = _SipHashState._rotateLeft(v1, by: 13)
50-
v1 ^= v0
51-
v0 = _SipHashState._rotateLeft(v0, by: 32)
52-
v2 = v2 &+ v3
53-
v3 = _SipHashState._rotateLeft(v3, by: 16)
54-
v3 ^= v2
55-
v0 = v0 &+ v3
56-
v3 = _SipHashState._rotateLeft(v3, by: 21)
57-
v3 ^= v0
58-
v2 = v2 &+ v1
59-
v1 = _SipHashState._rotateLeft(v1, by: 17)
60-
v1 ^= v2
61-
v2 = _SipHashState._rotateLeft(v2, by: 32)
62-
}
63-
64-
@inline(__always)
65-
fileprivate func _extract() -> UInt64 {
66-
return v0 ^ v1 ^ v2 ^ v3
67-
}
68-
}
69-
70-
// FIXME: Remove @usableFromInline and @_fixed_layout once Hasher is resilient.
71-
// rdar://problem/38549901
72-
@usableFromInline @_fixed_layout
73-
internal struct _SipHash13Core: _HasherCore {
74-
private var _state: _SipHashState
22+
extension Hasher {
23+
// FIXME: Remove @usableFromInline and @_fixed_layout once Hasher is resilient.
24+
// rdar://problem/38549901
25+
@usableFromInline @_fixed_layout
26+
internal struct _State {
27+
// "somepseudorandomlygeneratedbytes"
28+
fileprivate var v0: UInt64 = 0x736f6d6570736575
29+
fileprivate var v1: UInt64 = 0x646f72616e646f6d
30+
fileprivate var v2: UInt64 = 0x6c7967656e657261
31+
fileprivate var v3: UInt64 = 0x7465646279746573
32+
// The fields below are reserved for future use. They aren't currently used.
33+
fileprivate var v4: UInt64 = 0
34+
fileprivate var v5: UInt64 = 0
35+
fileprivate var v6: UInt64 = 0
36+
fileprivate var v7: UInt64 = 0
37+
38+
@inline(__always)
39+
fileprivate init(rawSeed: (UInt64, UInt64)) {
40+
v3 ^= rawSeed.1
41+
v2 ^= rawSeed.0
42+
v1 ^= rawSeed.1
43+
v0 ^= rawSeed.0
44+
}
7545

76-
@inline(__always)
77-
internal init(rawSeed: (UInt64, UInt64)) {
78-
_state = _SipHashState(rawSeed: rawSeed)
79-
}
46+
@inline(__always)
47+
fileprivate
48+
static func _rotateLeft(_ x: UInt64, by amount: UInt64) -> UInt64 {
49+
return (x &<< amount) | (x &>> (64 - amount))
50+
}
8051

81-
@inline(__always)
82-
internal mutating func compress(_ m: UInt64) {
83-
_state.v3 ^= m
84-
_state._round()
85-
_state.v0 ^= m
86-
}
52+
@inline(__always)
53+
fileprivate mutating func _round() {
54+
v0 = v0 &+ v1
55+
v1 = Hasher._State._rotateLeft(v1, by: 13)
56+
v1 ^= v0
57+
v0 = Hasher._State._rotateLeft(v0, by: 32)
58+
v2 = v2 &+ v3
59+
v3 = Hasher._State._rotateLeft(v3, by: 16)
60+
v3 ^= v2
61+
v0 = v0 &+ v3
62+
v3 = Hasher._State._rotateLeft(v3, by: 21)
63+
v3 ^= v0
64+
v2 = v2 &+ v1
65+
v1 = Hasher._State._rotateLeft(v1, by: 17)
66+
v1 ^= v2
67+
v2 = Hasher._State._rotateLeft(v2, by: 32)
68+
}
8769

88-
@inline(__always)
89-
internal mutating func finalize(tailAndByteCount: UInt64) -> UInt64 {
90-
compress(tailAndByteCount)
91-
_state.v2 ^= 0xff
92-
for _ in 0..<3 {
93-
_state._round()
70+
@inline(__always)
71+
fileprivate func _extract() -> UInt64 {
72+
return v0 ^ v1 ^ v2 ^ v3
9473
}
95-
return _state._extract()
9674
}
9775
}
9876

99-
internal struct _SipHash24Core: _HasherCore {
100-
private var _state: _SipHashState
101-
102-
@inline(__always)
103-
internal init(rawSeed: (UInt64, UInt64)) {
104-
_state = _SipHashState(rawSeed: rawSeed)
105-
}
77+
extension Hasher {
78+
// FIXME: Remove @usableFromInline and @_fixed_layout once Hasher is resilient.
79+
// rdar://problem/38549901
80+
@usableFromInline @_fixed_layout
81+
internal struct _Core: _HasherCore {
82+
private var _state: Hasher._State
10683

107-
@inline(__always)
108-
internal mutating func compress(_ m: UInt64) {
109-
_state.v3 ^= m
110-
_state._round()
111-
_state._round()
112-
_state.v0 ^= m
113-
}
114-
115-
@inline(__always)
116-
internal mutating func finalize(tailAndByteCount: UInt64) -> UInt64 {
117-
compress(tailAndByteCount)
84+
@inline(__always)
85+
internal init(rawSeed: (UInt64, UInt64)) {
86+
_state = Hasher._State(rawSeed: rawSeed)
87+
}
11888

119-
_state.v2 ^= 0xff
120-
for _ in 0..<4 {
89+
@inline(__always)
90+
internal mutating func compress(_ m: UInt64) {
91+
_state.v3 ^= m
12192
_state._round()
93+
_state.v0 ^= m
12294
}
123-
return _state._extract()
124-
}
125-
}
126-
127-
// FIXME: This type only exists to facilitate testing, and should not exist in
128-
// production builds.
129-
@usableFromInline // @testable
130-
internal struct _SipHash13 {
131-
internal typealias Core = _SipHash13Core
132-
133-
internal var _core: _BufferingHasher<Core>
134-
135-
@usableFromInline // @testable
136-
internal init(_rawSeed: (UInt64, UInt64)) {
137-
_core = _BufferingHasher(core: Core(rawSeed: _rawSeed))
138-
}
139-
@usableFromInline // @testable
140-
internal mutating func _combine(_ v: UInt) { _core.combine(v) }
141-
@usableFromInline // @testable
142-
internal mutating func _combine(_ v: UInt64) { _core.combine(v) }
143-
@usableFromInline // @testable
144-
internal mutating func _combine(_ v: UInt32) { _core.combine(v) }
145-
@usableFromInline // @testable
146-
internal mutating func _combine(_ v: UInt16) { _core.combine(v) }
147-
@usableFromInline // @testable
148-
internal mutating func _combine(_ v: UInt8) { _core.combine(v) }
149-
@usableFromInline // @testable
150-
internal mutating func _combine(bytes v: UInt64, count: Int) {
151-
_core.combine(bytes: v, count: count)
152-
}
153-
@usableFromInline // @testable
154-
internal mutating func combine(bytes: UnsafeRawBufferPointer) {
155-
_core.combine(bytes: bytes)
156-
}
157-
@usableFromInline // @testable
158-
internal __consuming func finalize() -> UInt64 {
159-
var core = _core
160-
return core.finalize()
161-
}
162-
}
163-
164-
// FIXME: This type only exists to facilitate testing, and should not exist in
165-
// production builds.
166-
@usableFromInline // @testable
167-
internal struct _SipHash24 {
168-
internal typealias Core = _SipHash24Core
169-
170-
internal var _core: _BufferingHasher<Core>
17195

172-
@usableFromInline // @testable
173-
internal init(_rawSeed: (UInt64, UInt64)) {
174-
_core = _BufferingHasher(core: Core(rawSeed: _rawSeed))
175-
}
176-
@usableFromInline // @testable
177-
internal mutating func _combine(_ v: UInt) { _core.combine(v) }
178-
@usableFromInline // @testable
179-
internal mutating func _combine(_ v: UInt64) { _core.combine(v) }
180-
@usableFromInline // @testable
181-
internal mutating func _combine(_ v: UInt32) { _core.combine(v) }
182-
@usableFromInline // @testable
183-
internal mutating func _combine(_ v: UInt16) { _core.combine(v) }
184-
@usableFromInline // @testable
185-
internal mutating func _combine(_ v: UInt8) { _core.combine(v) }
186-
@usableFromInline // @testable
187-
internal mutating func _combine(bytes v: UInt64, count: Int) {
188-
_core.combine(bytes: v, count: count)
189-
}
190-
@usableFromInline // @testable
191-
internal mutating func combine(bytes: UnsafeRawBufferPointer) {
192-
_core.combine(bytes: bytes)
193-
}
194-
@usableFromInline // @testable
195-
internal __consuming func finalize() -> UInt64 {
196-
var core = _core
197-
return core.finalize()
96+
@inline(__always)
97+
internal mutating func finalize(tailAndByteCount: UInt64) -> UInt64 {
98+
compress(tailAndByteCount)
99+
_state.v2 ^= 0xff
100+
for _ in 0..<3 {
101+
_state._round()
102+
}
103+
return _state._extract()
104+
}
198105
}
199106
}

stdlib/public/core/StringHashable.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@
1313
import SwiftShims
1414

1515
extension _UnmanagedString where CodeUnit == UInt8 {
16-
internal func hashASCII(into core: inout Hasher.Core) {
16+
internal func hashASCII(into core: inout Hasher._BufferingCore) {
1717
core.combine(bytes: rawBuffer)
1818
}
1919
}
2020

2121
extension BidirectionalCollection where Element == UInt16, SubSequence == Self {
22-
internal func hashUTF16(into core: inout Hasher.Core) {
22+
internal func hashUTF16(into core: inout Hasher._BufferingCore) {
2323
for i in self.indices {
2424
let cu = self[i]
2525
let cuIsASCII = cu <= 0x7F
@@ -62,7 +62,7 @@ extension _UnmanagedString where CodeUnit == UInt16 {
6262
}
6363

6464
internal func _rawHashValue(seed: Int) -> Int {
65-
var core = Hasher.Core(seed: seed)
65+
var core = Hasher._BufferingCore(seed: seed)
6666
self.hashUTF16(into: &core)
6767
return Int(truncatingIfNeeded: core.finalize())
6868
}
@@ -75,7 +75,7 @@ extension _UnmanagedOpaqueString {
7575
}
7676

7777
internal func _rawHashValue(seed: Int) -> Int {
78-
var core = Hasher.Core(seed: seed)
78+
var core = Hasher._BufferingCore(seed: seed)
7979
self.hashUTF16(into: &core)
8080
return Int(truncatingIfNeeded: core.finalize())
8181
}

0 commit comments

Comments
 (0)