3
3
4
4
package maphash
5
5
6
- // used in hash{32,64}.go to seed the hash function
7
- var hashkey [4 ]uint32
6
+ import (
7
+ _ "unsafe" // for linkname
8
+ )
9
+
10
+ // hashkey is similar how it is defined in runtime/alg.go for Go 1.19
11
+ // to be used in hash{32,64}.go to seed the hash function as part of
12
+ // runtime_memhash. We're using locally defined memhash so it got moved here.
13
+ var hashkey [3 ]uint32
8
14
9
15
func init () {
10
16
for i := range hashkey {
11
- hashkey [i ] = uint32 (runtime_fastrand64 ())
17
+ hashkey [i ] = runtime_fastrand () | 1
18
+ // The `| 1` is to make sure these numbers are odd
12
19
}
13
- hashkey [0 ] |= 1 // make sure these numbers are odd
14
- hashkey [1 ] |= 1
15
- hashkey [2 ] |= 1
16
- hashkey [3 ] |= 1
17
20
}
18
21
19
- func _rthash (b []byte , seed uint64 ) uint64 {
22
+ //go:linkname runtime_fastrand runtime.fastrand
23
+ func runtime_fastrand () uint32
24
+
25
+ // Bytes uses less efficient equivalent to avoid using unsafe.
26
+ func Bytes (seed Seed , b []byte ) uint64 {
27
+ var h Hash
28
+ h .SetSeed (seed )
29
+ _ , _ = h .Write (b )
30
+ return h .Sum64 ()
31
+ }
32
+
33
+ // String uses less efficient equivalent to avoid using unsafe.
34
+ func String (seed Seed , s string ) uint64 {
35
+ var h Hash
36
+ h .SetSeed (seed )
37
+ _ , _ = h .WriteString (s )
38
+ return h .Sum64 ()
39
+ }
40
+
41
+ // rthash is similar to the Go 1.19.13 version
42
+ // with the call to memhash changed to not use unsafe pointers.
43
+ func rthash (b []byte , seed uint64 ) uint64 {
20
44
if len (b ) == 0 {
21
45
return seed
22
46
}
23
47
// The runtime hasher only works on uintptr. Since GopherJS implements a
24
48
// 32-bit environment, we use two parallel hashers on the lower and upper 32
25
49
// bits.
26
- lo := memhash (b , uint32 (seed ), uint32 ( len ( b )) )
27
- hi := memhash (b , uint32 (seed >> 32 ), uint32 ( len ( b )) )
50
+ lo := memhash (b , uint32 (seed ))
51
+ hi := memhash (b , uint32 (seed >> 32 ))
28
52
return uint64 (hi )<< 32 | uint64 (lo )
29
53
}
30
54
55
+ //gopherjs:purge to remove link using unsafe pointers, use memhash instead.
56
+ func runtime_memhash ()
57
+
31
58
// The implementation below is adapted from the upstream runtime/hash32.go
32
59
// and avoids use of unsafe, which GopherJS doesn't support well and leads to
33
60
// worse performance.
@@ -38,8 +65,9 @@ func _rthash(b []byte, seed uint64) uint64 {
38
65
//
39
66
// Hashing algorithm inspired by wyhash:
40
67
// https://github.com/wangyi-fudan/wyhash/blob/ceb019b530e2c1c14d70b79bfa2bc49de7d95bc1/Modern%20Non-Cryptographic%20Hash%20Function%20and%20Pseudorandom%20Number%20Generator.pdf
41
- func memhash (p []byte , seed uint32 , s uint32 ) uintptr {
42
- a , b := mix32 (uint32 (seed ), uint32 (s ^ hashkey [0 ]))
68
+ func memhash (p []byte , seed uint32 ) uintptr {
69
+ s := len (p )
70
+ a , b := mix32 (uint32 (seed ), uint32 (s )^ hashkey [0 ])
43
71
if s == 0 {
44
72
return uintptr (a ^ b )
45
73
}
@@ -63,7 +91,7 @@ func memhash(p []byte, seed uint32, s uint32) uintptr {
63
91
return uintptr (a ^ b )
64
92
}
65
93
66
- func add (p []byte , x uint32 ) []byte {
94
+ func add (p []byte , x int ) []byte {
67
95
return p [x :]
68
96
}
69
97
@@ -80,51 +108,59 @@ func mix32(a, b uint32) (uint32, uint32) {
80
108
/*
81
109
The following functions were modified in Go 1.17 to improve performance,
82
110
but at the expense of being unsafe, and thus incompatible with GopherJS.
83
- To compensate, we have reverted these to the unoptimized Go 1.16 versions
84
- for now.
111
+ See https://cs.opensource.google/go/go/+/refs/tags/go1.19.13:src/hash/maphash/maphash.go;
112
+ To compensate, we use a simplified version of each method from Go 1.19.13,
113
+ similar to Go 1.16's versions, with the call to rthash changed to not use unsafe pointers.
85
114
86
115
See upstream issue https://github.com/golang/go/issues/47342 to implement
87
116
a purego version of this package, which should render this hack (and
88
117
likely this entire file) obsolete.
89
118
*/
90
119
91
- // Write is borrowed from Go 1.16 .
120
+ // Write is a simplification from Go 1.19 changed to not use unsafe .
92
121
func (h * Hash ) Write (b []byte ) (int , error ) {
93
122
size := len (b )
94
- for h .n + len (b ) > len (h .buf ) {
95
- k := copy (h .buf [h .n :], b )
96
- h .n = len (h .buf )
97
- b = b [k :]
98
- h .flush ()
123
+ if h .n + len (b ) > bufSize {
124
+ h .initSeed ()
125
+ for h .n + len (b ) > bufSize {
126
+ k := copy (h .buf [h .n :], b )
127
+ h .state .s = rthash (h .buf [:], h .state .s )
128
+ b = b [k :]
129
+ h .n = 0
130
+ }
99
131
}
100
132
h .n += copy (h .buf [h .n :], b )
101
133
return size , nil
102
134
}
103
135
104
- // WriteString is borrowed from Go 1.16 .
136
+ // WriteString is a simplification from Go 1.19 changed to not use unsafe .
105
137
func (h * Hash ) WriteString (s string ) (int , error ) {
106
138
size := len (s )
107
- for h .n + len (s ) > len (h .buf ) {
108
- k := copy (h .buf [h .n :], s )
109
- h .n = len (h .buf )
110
- s = s [k :]
111
- h .flush ()
139
+ if h .n + len (s ) > bufSize {
140
+ h .initSeed ()
141
+ for h .n + len (s ) > bufSize {
142
+ k := copy (h .buf [h .n :], s )
143
+ h .state .s = rthash (h .buf [:], h .state .s )
144
+ s = s [k :]
145
+ h .n = 0
146
+ }
112
147
}
113
148
h .n += copy (h .buf [h .n :], s )
114
149
return size , nil
115
150
}
116
151
152
+ // flush is the Go 1.19 version changed to not use unsafe.
117
153
func (h * Hash ) flush () {
118
154
if h .n != len (h .buf ) {
119
155
panic ("maphash: flush of partially full buffer" )
120
156
}
121
157
h .initSeed ()
122
- h .state .s = _rthash (h .buf [:], h .state .s )
158
+ h .state .s = rthash (h .buf [:], h .state .s )
123
159
h .n = 0
124
160
}
125
161
126
- // Sum64 is borrowed from Go 1.16 .
162
+ // Sum64 is the Go 1.19 version changed to not use unsafe .
127
163
func (h * Hash ) Sum64 () uint64 {
128
164
h .initSeed ()
129
- return _rthash (h .buf [:h .n ], h .state .s )
165
+ return rthash (h .buf [:h .n ], h .state .s )
130
166
}
0 commit comments