-
Notifications
You must be signed in to change notification settings - Fork 10.4k
/
HashingPrototype.swift
174 lines (137 loc) · 4.58 KB
/
HashingPrototype.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
// RUN: %target-run-simple-swift
// REQUIRES: executable_test
// REQUIRES: objc_interop
/*
<rdar://problem/14196462> Hashing in the standard library
New Hashing APIs
================
This is a proposal for new hashing APIs in the Swift standard library.
Current stdlib design has the Hashable protocol with 'hashValue: Int' property,
and the library leaves it up to the user how to implement it. In the proposed
design in the 99% case the user only has to enumerate the properties they want
to be included in the hash, and the Swift library will compute a good hash.
Problem
=======
Threading a single Hasher through all computations reduces the cost to set up
and finalize hash value computation. Unfortunately, it is not clear how to
allow this combineIntoHash() API to interoperate with Objective-C, in
particular, with Objective-C subclasses of Swift Hashable classes. See FIXME
below.
*/
protocol NewHashable /*: Equatable*/ {
func combineIntoHash<H : Hasher>(_ hasher: inout H)
}
struct UserTypeA : NewHashable {
var a1: Int
var a2: Float
func combineIntoHash<H : Hasher>(_ hasher: inout H) {
hasher.combine(a1)
hasher.combine(a2)
}
}
struct UserTypeB : NewHashable {
var b1: Int
var b2: UserTypeA // User-defined hashable type
var b3: [Int]
func combineIntoHash<H : Hasher>(_ hasher: inout H) {
hasher.combine(b1)
hasher.combine(b2)
hasher.combineSequence(b3)
}
}
class UserClassA : NSObject {
var a1: Int = 0
// error: declarations from extensions cannot be overridden yet
//func combineIntoHash<H : Hasher>(_ hasher: inout H) {
// hasher.combine(a1)
//}
// FIXME: Problem: what method should a derived Objective-C subclass
// override? 'combineIntoHash' is not @objc.
}
//===----------------------------------------------------------------------===//
// Implementation
//===----------------------------------------------------------------------===//
/// A hasher object computes a hash value.
///
/// Precondition: two hasher objects compute the same hash value when
/// the same sequence of `combine(...)` calls with equal arguments is
/// performed on both of them.
protocol Hasher {
//
// Primary APIs
//
mutating func combine(_ value: Int)
mutating func combine(_ value: Float)
// ... overloads for other primitive types...
mutating func squeezeHashValue<I : SignedInteger>(
_ resultRange: Range<I>) -> I
mutating func squeezeHashValue<I : UnsignedInteger>(
_ resultRange: Range<I>) -> I
//
// Convenience APIs; would be completely implemented by default
// implementations if we had them.
//
// This handles arrays, UnsafeBufferPointer, user-defined
// collections.
mutating func combineSequence<
S : Sequence
>(_ s: S)
where S.Iterator.Element : NewHashable
mutating func combine<H : NewHashable>(_ value: H)
}
/// A hasher for in-process, non-persistent hashtables.
struct InProcessHashtableHasher : Hasher {
// Only for exposition.
var _state: Int
init() {
// Should initialize to per-process seed.
_state = 0
}
mutating func combine(_ value: Int) {
// Only for exposition.
_state = _state ^ value
}
mutating func combine(_ value: Float) {
// Only for exposition.
_state = _state ^ Int(value.bitPattern)
}
mutating func combineSequence<
S : Sequence
>(_ s: S)
where S.Iterator.Element : NewHashable {
for v in s {
v.combineIntoHash(&self)
}
}
mutating func combine<H : NewHashable>(_ value: H) {
value.combineIntoHash(&self)
}
mutating func squeezeHashValue<I : SignedInteger>(
_ resultRange: Range<I>) -> I {
// ... finalize hash value computation first...
return I(IntMax(_state)) // Should actually clamp the value
}
mutating func squeezeHashValue<I : UnsignedInteger>(
_ resultRange: Range<I>) -> I {
// ... finalize hash value computation first...
return I(UIntMax(_state)) // Should actually clamp the value
}
}
/// A hasher with 128-bit output and a well-defined algorithm stable
/// *across platforms*; useful for on-disk or distributed hash tables.
/// Not a cryptographic hash.
// struct StableFingerprint128Hasher : Hasher {}
extension Int : NewHashable {
func combineIntoHash<H : Hasher>(_ hasher: inout H) {
hasher.combine(self)
}
}
//===----------------------------------------------------------------------===//
// Foundation overlay: interoperability with NSObject.hash
//===----------------------------------------------------------------------===//
import Foundation
extension NSObject : NewHashable {
func combineIntoHash<H : Hasher>(_ hasher: inout H) {
hasher.combine(self.hash)
}
}