@@ -10,41 +10,50 @@ import { smi } from './Math';
10
10
const defaultValueOf = Object . prototype . valueOf ;
11
11
12
12
export function hash ( o ) {
13
- switch ( typeof o ) {
13
+ if ( o == null ) {
14
+ return hashNullish ( o ) ;
15
+ }
16
+
17
+ if ( typeof o . hashCode === 'function' ) {
18
+ // Drop any high bits from accidentally long hash codes.
19
+ return smi ( o . hashCode ( o ) ) ;
20
+ }
21
+
22
+ const v = valueOf ( o ) ;
23
+
24
+ if ( v == null ) {
25
+ return hashNullish ( v ) ;
26
+ }
27
+
28
+ switch ( typeof v ) {
14
29
case 'boolean' :
15
30
// The hash values for built-in constants are a 1 value for each 5-byte
16
31
// shift region expect for the first, which encodes the value. This
17
32
// reduces the odds of a hash collision for these common values.
18
- return o ? 0x42108421 : 0x42108420 ;
33
+ return v ? 0x42108421 : 0x42108420 ;
19
34
case 'number' :
20
- return hashNumber ( o ) ;
35
+ return hashNumber ( v ) ;
21
36
case 'string' :
22
- return o . length > STRING_HASH_CACHE_MIN_STRLEN
23
- ? cachedHashString ( o )
24
- : hashString ( o ) ;
37
+ return v . length > STRING_HASH_CACHE_MIN_STRLEN
38
+ ? cachedHashString ( v )
39
+ : hashString ( v ) ;
25
40
case 'object' :
26
41
case 'function' :
27
- if ( o === null ) {
28
- return 0x42108422 ;
29
- }
30
- if ( typeof o . hashCode === 'function' ) {
31
- // Drop any high bits from accidentally long hash codes.
32
- return smi ( o . hashCode ( o ) ) ;
33
- }
34
- if ( o . valueOf !== defaultValueOf && typeof o . valueOf === 'function' ) {
35
- o = o . valueOf ( o ) ;
36
- }
37
- return hashJSObj ( o ) ;
38
- case 'undefined' :
39
- return 0x42108423 ;
42
+ return hashJSObj ( v ) ;
43
+ case 'symbol' :
44
+ return hashSymbol ( v ) ;
40
45
default :
41
- if ( typeof o . toString === 'function' ) {
42
- return hashString ( o . toString ( ) ) ;
46
+ if ( typeof v . toString === 'function' ) {
47
+ return hashString ( v . toString ( ) ) ;
43
48
}
44
- throw new Error ( 'Value type ' + typeof o + ' cannot be hashed.' ) ;
49
+ throw new Error ( 'Value type ' + typeof v + ' cannot be hashed.' ) ;
45
50
}
46
51
}
47
52
53
+ function hashNullish ( nullish ) {
54
+ return nullish === null ? 0x42108422 : /* undefined */ 0x42108423 ;
55
+ }
56
+
48
57
// Compress arbitrarily large numbers into smi hashes.
49
58
function hashNumber ( n ) {
50
59
if ( n !== n || n === Infinity ) {
@@ -90,6 +99,19 @@ function hashString(string) {
90
99
return smi ( hashed ) ;
91
100
}
92
101
102
+ function hashSymbol ( sym ) {
103
+ let hashed = symbolMap [ sym ] ;
104
+ if ( hashed !== undefined ) {
105
+ return hashed ;
106
+ }
107
+
108
+ hashed = nextHash ( ) ;
109
+
110
+ symbolMap [ sym ] = hashed ;
111
+
112
+ return hashed ;
113
+ }
114
+
93
115
function hashJSObj ( obj ) {
94
116
let hashed ;
95
117
if ( usingWeakMap ) {
@@ -116,10 +138,7 @@ function hashJSObj(obj) {
116
138
}
117
139
}
118
140
119
- hashed = ++ objHashUID ;
120
- if ( objHashUID & 0x40000000 ) {
121
- objHashUID = 0 ;
122
- }
141
+ hashed = nextHash ( ) ;
123
142
124
143
if ( usingWeakMap ) {
125
144
weakMap . set ( obj , hashed ) ;
@@ -186,14 +205,30 @@ function getIENodeHash(node) {
186
205
}
187
206
}
188
207
208
+ function valueOf ( obj ) {
209
+ return obj . valueOf !== defaultValueOf && typeof obj . valueOf === 'function'
210
+ ? obj . valueOf ( obj )
211
+ : obj ;
212
+ }
213
+
214
+ function nextHash ( ) {
215
+ const nextHash = ++ _objHashUID ;
216
+ if ( _objHashUID & 0x40000000 ) {
217
+ _objHashUID = 0 ;
218
+ }
219
+ return nextHash ;
220
+ }
221
+
189
222
// If possible, use a WeakMap.
190
223
const usingWeakMap = typeof WeakMap === 'function' ;
191
224
let weakMap ;
192
225
if ( usingWeakMap ) {
193
226
weakMap = new WeakMap ( ) ;
194
227
}
195
228
196
- let objHashUID = 0 ;
229
+ const symbolMap = Object . create ( null ) ;
230
+
231
+ let _objHashUID = 0 ;
197
232
198
233
let UID_HASH_KEY = '__immutablehash__' ;
199
234
if ( typeof Symbol === 'function' ) {
0 commit comments