Skip to content

Commit 8f2c13f

Browse files
committed
Add a prototype of new hashing APIs (so that it does not bit rot on my disk)
Unfortunately, I don't see how to ensure interoperability with Objective-C. Swift SVN r21581
1 parent 8f4e701 commit 8f2c13f

File tree

1 file changed

+170
-0
lines changed

1 file changed

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

0 commit comments

Comments
 (0)