-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathhashing.go
More file actions
153 lines (134 loc) · 4.56 KB
/
hashing.go
File metadata and controls
153 lines (134 loc) · 4.56 KB
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
package go_memoize
import (
"fmt"
"math"
)
// we are using FNV-1a hash algorithm to hash the key
// FNV (Fowler-Noll-Vo) hash algorithm has two main components:
// 1. offset basis
// The offset basis is the starting value of the hash.
// 2. prime multiplier
//The prime multiplier is a large prime number that is used to hash the input data.
// how it works:
// 1. The offset basis is multiplied by the prime multiplier.
// 2. The result is XORed with the first byte of the input data.
// 3. The result is then multiplied by the prime multiplier.
// 4. This process is repeated for each byte of the input data.
// 5. The final result is the hash value.
const (
// FNV-1a hash constants for 32-bit and 64-bit architectures
offset64 = uint64(14695981039346656037)
prime64 = uint64(1099511628211)
// for hashing boolean values
trueHash = offset64 ^ 1*prime64
falseHash = offset64 ^ 0*prime64
)
// hash1 hashes a single key using the FNV-1a algorithm.
// A is a comparable type.
func hash1[A comparable](key A) uint64 {
return hash(offset64, key)
}
// hash2 hashes two keys using the FNV-1a algorithm.
// A and B are comparable types.
func hash2[A, B comparable](key1 A, key2 B) uint64 {
return hash(hash(offset64, key1), key2)
}
// hash3 hashes three keys using the FNV-1a algorithm.
// A, B, and C are comparable types.
func hash3[A, B, C comparable](key1 A, key2 B, key3 C) uint64 {
return hash(hash(hash(offset64, key1), key2), key3)
}
// hash4 hashes four keys using the FNV-1a algorithm.
// A, B, C, and D are comparable types.
func hash4[A, B, C, D comparable](key1 A, key2 B, key3 C, key4 D) uint64 {
return hash(hash(hash(hash(offset64, key1), key2), key3), key4)
}
// hash5 hashes five keys using the FNV-1a algorithm.
// A, B, C, D, and E are comparable types.
func hash5[A, B, C, D, E comparable](key1 A, key2 B, key3 C, key4 D, key5 E) uint64 {
return hash(hash(hash(hash(hash(offset64, key1), key2), key3), key4), key5)
}
// hash6 hashes six keys using the FNV-1a algorithm.
// A, B, C, D, E, and F are comparable types.
func hash6[A, B, C, D, E, F comparable](key1 A, key2 B, key3 C, key4 D, key5 E, key6 F) uint64 {
return hash(hash(hash(hash(hash(hash(offset64, key1), key2), key3), key4), key5), key6)
}
// hash7 hashes seven keys using the FNV-1a algorithm.
// A, B, C, D, E, F, and G are comparable types.
func hash7[A, B, C, D, E, F, G comparable](key1 A, key2 B, key3 C, key4 D, key5 E, key6 F, key7 G) uint64 {
return hash(hash(hash(hash(hash(hash(hash(offset64, key1), key2), key3), key4), key5), key6), key7)
}
// hash hashes a key using the FNV-1a algorithm.
// A is a comparable type.
func hash[A comparable](hash uint64, key A) uint64 {
switch v := any(key).(type) {
case string:
return hashString(hash, v)
case int:
return hashInt(hash, uint64(v))
case int8:
return hashInt(hash, uint64(v))
case int16:
return hashInt(hash, uint64(v))
case int32:
return hashInt(hash, uint64(v))
case int64:
return hashInt(hash, uint64(v))
case uint:
return hashUint(hash, uint64(v))
case uint8:
return hashUint(hash, uint64(v))
case uint16:
return hashUint(hash, uint64(v))
case uint32:
return hashUint(hash, uint64(v))
case uint64:
return hashUint(hash, v)
case uintptr:
return hashUint(hash, uint64(v))
case float32:
return hashFloat(hash, math.Float64bits(float64(v)))
case float64:
return hashFloat(hash, math.Float64bits(v))
case bool:
return hashBool(v)
default:
panic(fmt.Sprintf("unsupported type for caching %T", key))
}
}
// hashString hashes a string key using the FNV-1a algorithm.
func hashString(hash uint64, key string) uint64 {
length := len(key)
// for loop unrolling
// Process four characters at a time
for i := 0; i < length/4*4; i += 4 {
hash = (hash ^ uint64(key[i])) * prime64
hash = (hash ^ uint64(key[i+1])) * prime64
hash = (hash ^ uint64(key[i+2])) * prime64
hash = (hash ^ uint64(key[i+3])) * prime64
}
// Process remaining characters
for i := length / 4 * 4; i < length; i++ {
hash = (hash ^ uint64(key[i])) * prime64
}
return hash
}
// hashInt hashes an integer key using the FNV-1a algorithm.
func hashInt(hash uint64, key uint64) uint64 {
return (hash ^ key) * prime64
}
// hashUint hashes an unsigned integer key using the FNV-1a algorithm.
func hashUint(hash uint64, key uint64) uint64 {
return (hash ^ key) * prime64
}
// hashFloat hashes a floating-point key using the FNV-1a algorithm.
func hashFloat(hash uint64, key uint64) uint64 {
return (hash ^ key) * prime64
}
// hashBool hashes a boolean key using the FNV-1a algorithm.
func hashBool(key bool) uint64 {
if key {
return trueHash
}
return falseHash
}