1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Z38\MurmurHash; |
4
|
|
|
|
5
|
|
|
class Murmur3F implements HashInterface |
6
|
|
|
{ |
7
|
|
|
private $seed; |
8
|
|
|
private $h1; |
9
|
|
|
private $h2; |
10
|
|
|
private $len; |
11
|
|
|
private $tail; |
12
|
|
|
private static $c1; |
13
|
|
|
private static $c2; |
14
|
|
|
private static $m1; |
15
|
|
|
private static $m2; |
16
|
|
|
private static $fmix1; |
17
|
|
|
private static $fmix2; |
18
|
|
|
private static $mask64; |
19
|
|
|
|
20
|
14 |
|
public function __construct($seed = 0) |
21
|
|
|
{ |
22
|
14 |
|
self::init(); |
23
|
14 |
|
$this->seed = $seed; |
24
|
14 |
|
$this->reset(); |
25
|
14 |
|
} |
26
|
|
|
|
27
|
14 |
|
public function reset() |
28
|
|
|
{ |
29
|
14 |
|
$this->h1 = $this->seed; |
30
|
14 |
|
$this->h2 = $this->seed; |
31
|
14 |
|
$this->len = 0; |
32
|
14 |
|
$this->tail = ''; |
33
|
14 |
|
} |
34
|
|
|
|
35
|
14 |
|
public function write($data) |
36
|
|
|
{ |
37
|
14 |
|
$this->len += strlen($data); |
38
|
14 |
|
$this->tail .= $data; |
39
|
14 |
|
while (strlen($this->tail) >= 16) { |
40
|
5 |
|
$k1 = gmp_init(bin2hex(strrev(substr($this->tail, 0, 8))), 16); |
41
|
5 |
|
$k2 = gmp_init(bin2hex(strrev(substr($this->tail, 8, 8))), 16); |
42
|
5 |
|
$this->tail = substr($this->tail, 16); |
43
|
|
|
|
44
|
5 |
|
$h1 = $this->h1; |
45
|
5 |
|
$h2 = $this->h2; |
46
|
|
|
|
47
|
5 |
|
$k1 = self::mul64($k1, self::$c1); |
48
|
5 |
|
$k1 = self::rotl64($k1, 31); |
49
|
5 |
|
$k1 = self::mul64($k1, self::$c2); |
50
|
5 |
|
$h1 = gmp_xor($h1, $k1); |
51
|
|
|
|
52
|
5 |
|
$h1 = self::rotl64($h1, 27); |
53
|
5 |
|
$h1 = self::add64($h1, $h2); |
54
|
5 |
|
$h1 = self::add64(gmp_mul(5, $h1), self::$m1); |
55
|
|
|
|
56
|
5 |
|
$k2 = self::mul64($k2, self::$c2); |
57
|
5 |
|
$k2 = self::rotl64($k2, 33); |
58
|
5 |
|
$k2 = self::mul64($k2, self::$c1); |
59
|
5 |
|
$h2 = gmp_xor($h2, $k2); |
60
|
|
|
|
61
|
5 |
|
$h2 = self::rotl64($h2, 31); |
62
|
5 |
|
$h2 = self::add64($h2, $h1); |
63
|
5 |
|
$h2 = self::add64(gmp_mul(5, $h2), self::$m2); |
64
|
|
|
|
65
|
5 |
|
$this->h1 = $h1; |
66
|
5 |
|
$this->h2 = $h2; |
67
|
5 |
|
} |
68
|
14 |
|
} |
69
|
|
|
|
70
|
14 |
|
public function sum() |
71
|
|
|
{ |
72
|
14 |
|
$h1 = $this->h1; |
73
|
14 |
|
$h2 = $this->h2; |
74
|
|
|
|
75
|
14 |
|
$k1 = 0; |
76
|
14 |
|
$k2 = 0; |
77
|
|
|
|
78
|
14 |
|
$tail = array_values(unpack('C*', $this->tail)); |
79
|
14 |
|
$len = count($tail); |
80
|
14 |
|
for ($i = $len; $i > 8; --$i) { |
81
|
7 |
|
$k2 = gmp_xor($k2, self::shiftl64($tail[$i - 1], ($i - 9) * 8)); |
82
|
7 |
|
} |
83
|
14 |
|
if ($len > 8) { |
84
|
7 |
|
$k2 = self::mul64($k2, self::$c2); |
85
|
7 |
|
$k2 = self::rotl64($k2, 33); |
86
|
7 |
|
$k2 = self::mul64($k2, self::$c1); |
87
|
7 |
|
$h2 = gmp_xor($h2, $k2); |
88
|
7 |
|
} |
89
|
14 |
|
for ($i = min($len, 8); $i > 0; --$i) { |
90
|
13 |
|
$k1 = gmp_xor($k1, self::shiftl64($tail[$i - 1], ($i - 1) * 8)); |
91
|
13 |
|
} |
92
|
14 |
|
if ($len > 0) { |
93
|
13 |
|
$k1 = self::mul64($k1, self::$c1); |
94
|
13 |
|
$k1 = self::rotl64($k1, 31); |
95
|
13 |
|
$k1 = self::mul64($k1, self::$c2); |
96
|
13 |
|
$h1 = gmp_xor($h1, $k1); |
97
|
13 |
|
} |
98
|
|
|
|
99
|
14 |
|
$h1 = gmp_xor($h1, $this->len); |
100
|
14 |
|
$h2 = gmp_xor($h2, $this->len); |
101
|
14 |
|
$h1 = self::add64($h1, $h2); |
102
|
14 |
|
$h2 = self::add64($h2, $h1); |
103
|
14 |
|
$h1 = self::fmix64($h1); |
104
|
14 |
|
$h2 = self::fmix64($h2); |
105
|
14 |
|
$h1 = self::add64($h1, $h2); |
106
|
14 |
|
$h2 = self::add64($h2, $h1); |
107
|
|
|
|
108
|
14 |
|
return self::export64($h1).self::export64($h2); |
109
|
|
|
} |
110
|
|
|
|
111
|
14 |
|
private static function init() |
112
|
|
|
{ |
113
|
14 |
|
if (self::$c1 !== null) { |
114
|
13 |
|
return; |
115
|
|
|
} |
116
|
|
|
|
117
|
1 |
|
self::$c1 = gmp_init('0x87c37b91114253d5'); |
118
|
1 |
|
self::$c2 = gmp_init('0x4cf5ad432745937f'); |
119
|
1 |
|
self::$m1 = gmp_init('0x52dce729'); |
120
|
1 |
|
self::$m2 = gmp_init('0x38495ab5'); |
121
|
1 |
|
self::$fmix1 = gmp_init('0xff51afd7ed558ccd'); |
122
|
1 |
|
self::$fmix2 = gmp_init('0xc4ceb9fe1a85ec53'); |
123
|
1 |
|
self::$mask64 = gmp_init('0xffffffffffffffff'); |
124
|
1 |
|
} |
125
|
|
|
|
126
|
14 |
|
private static function fmix64($x) |
127
|
|
|
{ |
128
|
14 |
|
$x = gmp_xor($x, self::shiftr($x, 33)); |
129
|
14 |
|
$x = self::mul64($x, self::$fmix1); |
130
|
14 |
|
$x = gmp_xor($x, self::shiftr($x, 33)); |
131
|
14 |
|
$x = self::mul64($x, self::$fmix2); |
132
|
14 |
|
$x = gmp_xor($x, self::shiftr($x, 33)); |
133
|
|
|
|
134
|
14 |
|
return $x; |
135
|
|
|
} |
136
|
|
|
|
137
|
13 |
|
private static function rotl64($x, $r) |
138
|
|
|
{ |
139
|
13 |
|
return gmp_and(gmp_or( |
140
|
13 |
|
self::shiftl($x, $r), |
141
|
13 |
|
self::shiftr($x, 64 - $r) |
142
|
13 |
|
), self::$mask64); |
143
|
|
|
} |
144
|
|
|
|
145
|
14 |
|
private static function export64($x) |
146
|
|
|
{ |
147
|
14 |
|
return strrev(pack('H*', str_pad(gmp_strval($x, 16), 16, '0', STR_PAD_LEFT))); |
148
|
|
|
} |
149
|
|
|
|
150
|
13 |
|
private static function shiftl64($x, $shift) |
151
|
|
|
{ |
152
|
13 |
|
return gmp_and(self::shiftl($x, $shift), self::$mask64); |
153
|
|
|
} |
154
|
|
|
|
155
|
13 |
|
private static function shiftl($x, $shift) |
156
|
|
|
{ |
157
|
13 |
|
return gmp_mul($x, gmp_pow(2, $shift)); |
158
|
|
|
} |
159
|
|
|
|
160
|
14 |
|
private static function shiftr($x, $shift) |
161
|
|
|
{ |
162
|
14 |
|
return gmp_div($x, gmp_pow(2, $shift)); |
163
|
|
|
} |
164
|
|
|
|
165
|
14 |
|
private static function mul64($a, $b) |
166
|
|
|
{ |
167
|
14 |
|
return gmp_and(gmp_mul($a, $b), self::$mask64); |
168
|
|
|
} |
169
|
|
|
|
170
|
14 |
|
private static function add64($a, $b) |
171
|
|
|
{ |
172
|
14 |
|
return gmp_and(gmp_add($a, $b), self::$mask64); |
173
|
|
|
} |
174
|
|
|
} |
175
|
|
|
|