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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
|
discard """
targets: "c cpp js"
"""
import std/hashes
from stdtest/testutils import disableVm, whenVMorJs
when not defined(js) and not defined(cpp):
block:
var x = 12
iterator hello(): int {.closure.} =
yield x
discard hash(hello)
block hashes:
block hashing:
var dummy = 0.0
doAssert hash(dummy) == hash(-dummy)
# "VM and runtime should make the same hash value (hashIdentity)"
block:
const hi123 = hashIdentity(123)
doAssert hashIdentity(123) == hi123
# "VM and runtime should make the same hash value (hashWangYi1)"
block:
const wy123 = hashWangYi1(123)
doAssert wy123 != 0
doAssert hashWangYi1(123) == wy123
# "hashIdentity value incorrect at 456"
block:
doAssert hashIdentity(456) == 456
# "hashWangYi1 value incorrect at 456"
block:
when Hash.sizeof < 8:
doAssert hashWangYi1(456) == 1293320666
else:
doAssert hashWangYi1(456) == -6421749900419628582
block empty:
var
a = ""
b = newSeq[char]()
c = newSeq[int]()
d = cstring""
e = "abcd"
doAssert hash(a) == 0
doAssert hash(b) == 0
doAssert hash(c) == 0
doAssert hash(d) == 0
doAssert hashIgnoreCase(a) == 0
doAssert hashIgnoreStyle(a) == 0
doAssert hash(e, 3, 2) == 0
block sameButDifferent:
doAssert hash("aa bb aaaa1234") == hash("aa bb aaaa1234", 0, 13)
doAssert hash("aa bb aaaa1234") == hash(cstring"aa bb aaaa1234")
doAssert hashIgnoreCase("aA bb aAAa1234") == hashIgnoreCase("aa bb aaaa1234")
doAssert hashIgnoreStyle("aa_bb_AAaa1234") == hashIgnoreCase("aaBBAAAa1234")
block smallSize: # no multibyte hashing
let
xx = @['H', 'i']
ii = @[72'u8, 105]
ss = "Hi"
doAssert hash(xx) == hash(ii)
doAssert hash(xx) == hash(ss)
doAssert hash(xx) == hash(xx, 0, xx.high)
doAssert hash(ss) == hash(ss, 0, ss.high)
block largeSize: # longer than 4 characters
let
xx = @['H', 'e', 'l', 'l', 'o']
xxl = @['H', 'e', 'l', 'l', 'o', 'w', 'e', 'e', 'n', 's']
ssl = "Helloweens"
doAssert hash(xxl) == hash(ssl)
doAssert hash(xxl) == hash(xxl, 0, xxl.high)
doAssert hash(ssl) == hash(ssl, 0, ssl.high)
doAssert hash(xx) == hash(xxl, 0, 4)
doAssert hash(xx) == hash(ssl, 0, 4)
doAssert hash(xx, 0, 3) == hash(xxl, 0, 3)
doAssert hash(xx, 0, 3) == hash(ssl, 0, 3)
proc main() =
doAssert hash(0.0) == hash(0)
# bug #16061
doAssert hash(cstring"abracadabra") == 97309975
doAssert hash(cstring"abracadabra") == hash("abracadabra")
when sizeof(int) == 8 or defined(js):
block:
var s: seq[Hash]
for a in [0.0, 1.0, -1.0, 1000.0, -1000.0]:
let b = hash(a)
doAssert b notin s
s.add b
when defined(js):
doAssert hash(0.345602) == 2035867618
doAssert hash(234567.45) == -20468103
doAssert hash(-9999.283456) == -43247422
doAssert hash(84375674.0) == 707542256
else:
doAssert hash(0.345602) == 387936373221941218
doAssert hash(234567.45) == -8179139172229468551
doAssert hash(-9999.283456) == 5876943921626224834
doAssert hash(84375674.0) == 1964453089107524848
else:
doAssert hash(0.345602) != 0
doAssert hash(234567.45) != 0
doAssert hash(-9999.283456) != 0
doAssert hash(84375674.0) != 0
block: # bug #16555
proc fn(): auto =
# avoids hardcoding values
var a = "abc\0def"
var b = a.cstring
result = (hash(a), hash(b))
doAssert result[0] != result[1]
when not defined(js):
doAssert fn() == static(fn())
else:
# xxx this is a tricky case; consistency of hashes for cstring's containing
# '\0\' matters for c backend but less for js backend since such strings
# are much less common in js backend; we make vm for js backend consistent
# with c backend instead of js backend because FFI code (or other) could
# run at CT, expecting c semantics.
discard
block: # hash(object)
type
Obj = object
x: int
y: string
Obj2[T] = object
x: int
y: string
Obj3 = object
x: int
y: string
Obj4 = object
case t: bool
of false:
x: int
of true:
y: int
z: int
Obj5 = object
case t: bool
of false:
x: int
of true:
y: int
z: int
proc hash(a: Obj2): Hash = hash(a.x)
proc hash(a: Obj3): Hash = hash((a.x,))
proc hash(a: Obj5): Hash =
case a.t
of false: hash(a.x)
of true: hash(a.y)
doAssert hash(Obj(x: 520, y: "Nim")) != hash(Obj(x: 520, y: "Nim2"))
doAssert hash(Obj2[float](x: 520, y: "Nim")) == hash(Obj2[float](x: 520, y: "Nim2"))
doAssert hash(Obj2[float](x: 520, y: "Nim")) != hash(Obj2[float](x: 521, y: "Nim2"))
doAssert hash(Obj3(x: 520, y: "Nim")) == hash(Obj3(x: 520, y: "Nim2"))
doAssert hash(Obj4(t: false, x: 1)) == hash(Obj4(t: false, x: 1))
doAssert hash(Obj4(t: false, x: 1)) != hash(Obj4(t: false, x: 2))
doAssert hash(Obj4(t: false, x: 1)) != hash(Obj4(t: true, y: 1))
doAssert hash(Obj5(t: false, x: 1)) != hash(Obj5(t: false, x: 2))
doAssert hash(Obj5(t: false, x: 1)) == hash(Obj5(t: true, y: 1))
doAssert hash(Obj5(t: false, x: 1)) != hash(Obj5(t: true, y: 2))
block: # hash(ref|ptr|pointer)
var a: array[10, uint8]
# disableVm:
whenVMorJs:
# pending fix proposed in https://github.com/nim-lang/Nim/issues/15952#issuecomment-786312417
discard
do:
assert a[0].addr.hash != a[1].addr.hash
assert cast[pointer](a[0].addr).hash == a[0].addr.hash
block: # hash(ref)
type A = ref object
x: int
let a = A(x: 3)
disableVm: # xxx Error: VM does not support 'cast' from tyRef to tyPointer
let ha = a.hash
assert ha != A(x: 3).hash # A(x: 3) is a different ref object from `a`.
a.x = 4
assert ha == a.hash # the hash only depends on the address
static: main()
main()
|