meowhash 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Gemfile +7 -0
- data/LICENSE +20 -0
- data/Rakefile +34 -0
- data/ext/meowhash/extconf.rb +7 -0
- data/ext/meowhash/meow_hash.h +255 -0
- data/ext/meowhash/meow_intrinsics.h +261 -0
- data/ext/meowhash/meowhash.c +48 -0
- data/lib/meowhash.rb +1 -0
- data/meowhash.gemspec +17 -0
- data/test/test_meowhash.rb +28 -0
- metadata +83 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: c656d4783358e5a42260dd2485ec44f981d55f20ca3a844e0e93c5d9c0baf70a
|
4
|
+
data.tar.gz: e964dac5d53526c7db83cb832f12ff07e71269ab41fe76cbd10c4e8a13602311
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 53a5317408a40723ec248dc760641c8cc7679ded23ab634eb735f5c1c364941d6e8d994d2a00c6f4fc4cfd9074d45734c80c4e5295d8ce7388982babc6f11451
|
7
|
+
data.tar.gz: 38de55bc2e688a22fd4200c7edca5364810d8258a4592c315e4ceaa754b4aa87d31461974b33cfac5e866e44c91fe38a8c0e707587fe17d40bbf7187c0fc6528
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
zlib License
|
2
|
+
|
3
|
+
(C) Copyright 2018 Molly Rocket, Inc. (Meow Hash)
|
4
|
+
(C) Copyright 2018 Alex Snaps (wrapper)
|
5
|
+
|
6
|
+
This software is provided 'as-is', without any express or implied
|
7
|
+
warranty. In no event will the authors be held liable for any damages
|
8
|
+
arising from the use of this software.
|
9
|
+
|
10
|
+
Permission is granted to anyone to use this software for any purpose,
|
11
|
+
including commercial applications, and to alter it and redistribute it
|
12
|
+
freely, subject to the following restrictions:
|
13
|
+
|
14
|
+
1. The origin of this software must not be misrepresented; you must not
|
15
|
+
claim that you wrote the original software. If you use this software
|
16
|
+
in a product, an acknowledgment in the product documentation would be
|
17
|
+
appreciated but is not required.
|
18
|
+
2. Altered source versions must be plainly marked as such, and must not be
|
19
|
+
misrepresented as being the original software.
|
20
|
+
3. This notice may not be removed or altered from any source distribution.
|
data/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
task :default => :test
|
2
|
+
|
3
|
+
# ==========================================================
|
4
|
+
# Packaging
|
5
|
+
# ==========================================================
|
6
|
+
|
7
|
+
GEMSPEC = eval(File.read('meowhash.gemspec'))
|
8
|
+
|
9
|
+
require 'rubygems/package_task'
|
10
|
+
Gem::PackageTask.new(GEMSPEC) do |pkg|
|
11
|
+
end
|
12
|
+
|
13
|
+
# ==========================================================
|
14
|
+
# Ruby Extension
|
15
|
+
# ==========================================================
|
16
|
+
|
17
|
+
require 'rake/extensiontask'
|
18
|
+
Rake::ExtensionTask.new('meowhash', GEMSPEC) do |ext|
|
19
|
+
ext.ext_dir = 'ext/meowhash'
|
20
|
+
ext.lib_dir = 'lib/meowhash'
|
21
|
+
end
|
22
|
+
task :build => :compile
|
23
|
+
|
24
|
+
# ==========================================================
|
25
|
+
# Testing
|
26
|
+
# ==========================================================
|
27
|
+
|
28
|
+
require 'rake/testtask'
|
29
|
+
Rake::TestTask.new 'test' do |t|
|
30
|
+
t.test_files = FileList['test/test_*.rb']
|
31
|
+
end
|
32
|
+
task :test => :build
|
33
|
+
|
34
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,255 @@
|
|
1
|
+
/* ========================================================================
|
2
|
+
|
3
|
+
Meow - A Fast Non-cryptographic Hash
|
4
|
+
(C) Copyright 2018 by Molly Rocket, Inc. (https://mollyrocket.com)
|
5
|
+
|
6
|
+
See https://mollyrocket.com/meowhash for details.
|
7
|
+
|
8
|
+
========================================================================
|
9
|
+
|
10
|
+
zlib License
|
11
|
+
|
12
|
+
(C) Copyright 2018 Molly Rocket, Inc.
|
13
|
+
|
14
|
+
This software is provided 'as-is', without any express or implied
|
15
|
+
warranty. In no event will the authors be held liable for any damages
|
16
|
+
arising from the use of this software.
|
17
|
+
|
18
|
+
Permission is granted to anyone to use this software for any purpose,
|
19
|
+
including commercial applications, and to alter it and redistribute it
|
20
|
+
freely, subject to the following restrictions:
|
21
|
+
|
22
|
+
1. The origin of this software must not be misrepresented; you must not
|
23
|
+
claim that you wrote the original software. If you use this software
|
24
|
+
in a product, an acknowledgment in the product documentation would be
|
25
|
+
appreciated but is not required.
|
26
|
+
2. Altered source versions must be plainly marked as such, and must not be
|
27
|
+
misrepresented as being the original software.
|
28
|
+
3. This notice may not be removed or altered from any source distribution.
|
29
|
+
|
30
|
+
========================================================================
|
31
|
+
|
32
|
+
FAQ
|
33
|
+
|
34
|
+
Q: What is it?
|
35
|
+
|
36
|
+
A: Meow is a 128-bit non-cryptographic hash that operates at high speeds
|
37
|
+
on x64 and ARM processors that provide AES instructions. It is
|
38
|
+
designed to be truncatable to 64 and 32-bit hash values and still
|
39
|
+
retain good collision resistance.
|
40
|
+
|
41
|
+
Q: What is it GOOD for?
|
42
|
+
|
43
|
+
A: Quickly hashing any amount of data for comparison purposes such as
|
44
|
+
block deduplication or change detection. It is extremely fast on
|
45
|
+
all buffer sizes, from one byte to one gigabyte and up.
|
46
|
+
|
47
|
+
Q: What is it BAD for?
|
48
|
+
|
49
|
+
A: Anything security-related. It should be assumed that it provides
|
50
|
+
no protection from adversaries whatsoever. It is also not particularly
|
51
|
+
fast on processors that don't support AES instructions (eg., non-x64/ARM
|
52
|
+
processors).
|
53
|
+
|
54
|
+
Q: Why is it called the "Meow hash"?
|
55
|
+
|
56
|
+
A: It is named after a character in Meow the Infinite
|
57
|
+
(https://meowtheinfinite.com)
|
58
|
+
|
59
|
+
Q: Who wrote it?
|
60
|
+
|
61
|
+
A: CASEY MURATORI (https://caseymuratori.com) wrote the original
|
62
|
+
implementation for use in processing large-footprint assets for
|
63
|
+
the game 1935 (https://molly1935.com).
|
64
|
+
|
65
|
+
After the initial version, the hash was refined via collaboration
|
66
|
+
with several great programmers who contributed suggestions and
|
67
|
+
modifications:
|
68
|
+
|
69
|
+
JEFF ROBERTS (https://radgametools.com) provided a super slick
|
70
|
+
way to handle the residual end-of-buffer bytes that dramatically
|
71
|
+
improved Meow's small hash performance.
|
72
|
+
|
73
|
+
MARTINS MOZEIKO (https://matrins.ninja) ported Meow to ARM and
|
74
|
+
ANSI-C, and added the proper preprocessor dressing for clean
|
75
|
+
compilation on a variety of compiler configurations.
|
76
|
+
|
77
|
+
FABIAN GIESEN (https://fgiesen.wordpress.com) provided support
|
78
|
+
for getting the benchmarking working properly across a number
|
79
|
+
of platforms.
|
80
|
+
|
81
|
+
ARAS PRANCKEVICIUS (https://aras-p.info) provided the allocation
|
82
|
+
shim for compilation on Mac OS X.
|
83
|
+
|
84
|
+
========================================================================
|
85
|
+
|
86
|
+
USAGE
|
87
|
+
|
88
|
+
For a complete working example, see meow_example.cpp. Briefly:
|
89
|
+
|
90
|
+
// Include meow_intrinsics if you want it to detect platforms
|
91
|
+
// and define types and intrinsics for you. Omit it if you
|
92
|
+
// want to define them yourself.
|
93
|
+
#include "meow_intrinsics.h"
|
94
|
+
|
95
|
+
// Include meow_hash for the Meow hash function
|
96
|
+
#include "meow_hash.h"
|
97
|
+
|
98
|
+
// Hash a block of data using CPU-specific acceleration
|
99
|
+
meow_u128 MeowHash_Accelerated(u64 Seed, u64 Len, void *Source);
|
100
|
+
|
101
|
+
// Check if two Meow hashes are the same
|
102
|
+
// (returns zero if they aren't, non-zero if they are)
|
103
|
+
int MeowHashesAreEqual(meow_u128 A, meow_u128 B)
|
104
|
+
|
105
|
+
// Truncate a Meow hash to 64 bits
|
106
|
+
meow_u64 MeowU64From(meow_u128 Hash);
|
107
|
+
|
108
|
+
// Truncate a Meow hash to 32 bits
|
109
|
+
meow_u32 MeowU32From(meow_u128 Hash);
|
110
|
+
|
111
|
+
**** VERY IMPORTANT X64 COMPILATION NOTES ****
|
112
|
+
|
113
|
+
On x64, Meow uses the AESDEC instruction, which comes in two flavors:
|
114
|
+
SSE (aesdec) and AVX (vaesdec). If you are compiling _with_ AVX support,
|
115
|
+
your compiler will probably emit the AVX variant, which means your code
|
116
|
+
WILL NOT RUN on computers that do not have AVX. If you need to deploy
|
117
|
+
this hash on computers that do not have AVX, you must take care to
|
118
|
+
TURN OFF support for AVX in your compiler for the file that includes
|
119
|
+
the Meow hash!
|
120
|
+
|
121
|
+
======================================================================== */
|
122
|
+
|
123
|
+
//
|
124
|
+
// NOTE(casey): This version is EXPERIMENTAL. The Meow hash is still
|
125
|
+
// undergoing testing and finalization.
|
126
|
+
//
|
127
|
+
// **** EXPECT HASHES/APIs TO CHANGE UNTIL THE VERSION NUMBER HITS 1.0. ****
|
128
|
+
//
|
129
|
+
// You have been warned.
|
130
|
+
//
|
131
|
+
|
132
|
+
static const unsigned char MeowShiftAdjust[31] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128};
|
133
|
+
static const unsigned char MeowMaskLen[32] = {255,255,255,255, 255,255,255,255, 255,255,255,255, 255,255,255,255, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0};
|
134
|
+
|
135
|
+
// TODO(casey): These constants are loaded to initialize the lanes. Jacob should
|
136
|
+
// give us some feedback on what they should _actually_ be set to.
|
137
|
+
#define MEOW_S0_INIT { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11, 12,13,14,15}
|
138
|
+
#define MEOW_S1_INIT {16,17,18,19, 20,21,22,23, 24,25,26,27, 28,29,30,31}
|
139
|
+
#define MEOW_S2_INIT {32,33,34,35, 36,37,38,39, 40,41,42,43, 44,45,46,47}
|
140
|
+
#define MEOW_S3_INIT {48,49,50,51, 52,53,54,55, 56,57,58,59, 60,61,62,63}
|
141
|
+
static const unsigned char MeowS0Init[] = MEOW_S0_INIT;
|
142
|
+
static const unsigned char MeowS1Init[] = MEOW_S1_INIT;
|
143
|
+
static const unsigned char MeowS2Init[] = MEOW_S2_INIT;
|
144
|
+
static const unsigned char MeowS3Init[] = MEOW_S3_INIT;
|
145
|
+
|
146
|
+
//
|
147
|
+
// NOTE(casey): 128-wide AES-NI Meow (maximum of 16 bytes/clock single threaded)
|
148
|
+
//
|
149
|
+
|
150
|
+
static meow_hash
|
151
|
+
MeowHash_Accelerated(meow_u64 Seed, meow_u64 TotalLengthInBytes, void *SourceInit)
|
152
|
+
{
|
153
|
+
//
|
154
|
+
// NOTE(casey): Initialize the four AES streams and the mixer
|
155
|
+
//
|
156
|
+
|
157
|
+
meow_aes_128 S0 = Meow128_GetAESConstant(MeowS0Init);
|
158
|
+
meow_aes_128 S1 = Meow128_GetAESConstant(MeowS1Init);
|
159
|
+
meow_aes_128 S2 = Meow128_GetAESConstant(MeowS2Init);
|
160
|
+
meow_aes_128 S3 = Meow128_GetAESConstant(MeowS3Init);
|
161
|
+
|
162
|
+
meow_u128 Mixer = Meow128_Set64x2(Seed - TotalLengthInBytes,
|
163
|
+
Seed + TotalLengthInBytes + 1);
|
164
|
+
|
165
|
+
//
|
166
|
+
// NOTE(casey): Handle as many full 256-byte blocks as possible
|
167
|
+
//
|
168
|
+
|
169
|
+
meow_u8 *Source = (meow_u8 *)SourceInit;
|
170
|
+
meow_u64 Len = TotalLengthInBytes;
|
171
|
+
int unsigned Len8 = Len & 15;
|
172
|
+
int unsigned Len128 = Len & 48;
|
173
|
+
|
174
|
+
while(Len >= 64)
|
175
|
+
{
|
176
|
+
S0 = Meow128_AESDEC_Mem(S0, Source);
|
177
|
+
S1 = Meow128_AESDEC_Mem(S1, Source + 16);
|
178
|
+
S2 = Meow128_AESDEC_Mem(S2, Source + 32);
|
179
|
+
S3 = Meow128_AESDEC_Mem(S3, Source + 48);
|
180
|
+
|
181
|
+
Len -= 64;
|
182
|
+
Source += 64;
|
183
|
+
}
|
184
|
+
|
185
|
+
//
|
186
|
+
// NOTE(casey): Overhanging individual bytes
|
187
|
+
//
|
188
|
+
|
189
|
+
if(Len8)
|
190
|
+
{
|
191
|
+
meow_u8 *Overhang = Source + Len128;
|
192
|
+
int Align = ((int)(meow_umm)Overhang) & 15;
|
193
|
+
if(Align)
|
194
|
+
{
|
195
|
+
int End = ((int)(meow_umm)Overhang) & (MEOW_PAGESIZE - 1);
|
196
|
+
|
197
|
+
// NOTE(jeffr): If we are nowhere near the page end, use full unaligned load (cmov to set)
|
198
|
+
if (End <= (MEOW_PAGESIZE - 16))
|
199
|
+
{
|
200
|
+
Align = 0;
|
201
|
+
}
|
202
|
+
|
203
|
+
// NOTE(jeffr): If we will read over the page end, use a full unaligned load (cmov to set)
|
204
|
+
if ((End + Len8) > MEOW_PAGESIZE)
|
205
|
+
{
|
206
|
+
Align = 0;
|
207
|
+
}
|
208
|
+
|
209
|
+
meow_u128 Partial = Meow128_Shuffle_Mem(Overhang - Align, &MeowShiftAdjust[Align]);
|
210
|
+
|
211
|
+
Partial = Meow128_And_Mem( Partial, &MeowMaskLen[16 - Len8] );
|
212
|
+
S3 = Meow128_AESDEC(S3, Partial);
|
213
|
+
}
|
214
|
+
else
|
215
|
+
{
|
216
|
+
// NOTE(casey): We don't have to do Jeff's heroics when we know the
|
217
|
+
// buffer is aligned, since we cannot span a memory page (by definition).
|
218
|
+
meow_u128 Partial = Meow128_And_Mem(*(meow_u128 *)Overhang, &MeowMaskLen[16 - Len8]);
|
219
|
+
S3 = Meow128_AESDEC(S3, Partial);
|
220
|
+
}
|
221
|
+
}
|
222
|
+
|
223
|
+
//
|
224
|
+
// NOTE(casey): Overhanging full 128-bit lanes
|
225
|
+
//
|
226
|
+
|
227
|
+
switch(Len128)
|
228
|
+
{
|
229
|
+
case 48: S2 = Meow128_AESDEC_Mem(S2, Source + 32);
|
230
|
+
case 32: S1 = Meow128_AESDEC_Mem(S1, Source + 16);
|
231
|
+
case 16: S0 = Meow128_AESDEC_Mem(S0, Source);
|
232
|
+
}
|
233
|
+
|
234
|
+
//
|
235
|
+
// NOTE(casey): Mix the four lanes down to one 128-bit hash
|
236
|
+
//
|
237
|
+
|
238
|
+
S3 = Meow128_AESDEC(S3, Mixer);
|
239
|
+
S2 = Meow128_AESDEC(S2, Mixer);
|
240
|
+
S1 = Meow128_AESDEC(S1, Mixer);
|
241
|
+
S0 = Meow128_AESDEC(S0, Mixer);
|
242
|
+
|
243
|
+
S2 = Meow128_AESDEC(S2, Meow128_AESDEC_Finalize(S3));
|
244
|
+
S0 = Meow128_AESDEC(S0, Meow128_AESDEC_Finalize(S1));
|
245
|
+
|
246
|
+
S2 = Meow128_AESDEC(S2, Mixer);
|
247
|
+
|
248
|
+
S0 = Meow128_AESDEC(S0, Meow128_AESDEC_Finalize(S2));
|
249
|
+
S0 = Meow128_AESDEC(S0, Mixer);
|
250
|
+
|
251
|
+
meow_hash Result;
|
252
|
+
Meow128_CopyToHash(Meow128_AESDEC_Finalize(S0), Result);
|
253
|
+
|
254
|
+
return(Result);
|
255
|
+
}
|
@@ -0,0 +1,261 @@
|
|
1
|
+
/* ========================================================================
|
2
|
+
|
3
|
+
meow_intrinsics.h
|
4
|
+
(C) Copyright 2018 by Molly Rocket, Inc. (https://mollyrocket.com)
|
5
|
+
|
6
|
+
See https://mollyrocket.com/meowhash for details.
|
7
|
+
|
8
|
+
This is the default way to define all of the types and operations that
|
9
|
+
meow_hash.h needs. However, if you've got your _own_ equivalent type
|
10
|
+
definitions and intrinsics, you can _omit_ this header file and just
|
11
|
+
#define/typedef all the Meow ops to map to your own ops, keeping things
|
12
|
+
nice and uniform in your codebase.
|
13
|
+
|
14
|
+
======================================================================== */
|
15
|
+
|
16
|
+
#if !defined(MEOW_HASH_INTRINSICS_H)
|
17
|
+
|
18
|
+
//
|
19
|
+
// NOTE(casey): Try to guess the source file for compiler intrinsics
|
20
|
+
//
|
21
|
+
#if _MSC_VER
|
22
|
+
|
23
|
+
#if _M_AMD64 || _M_IX86
|
24
|
+
#include <intrin.h>
|
25
|
+
#elif _M_ARM64
|
26
|
+
#include <arm64_neon.h>
|
27
|
+
#endif
|
28
|
+
|
29
|
+
#else
|
30
|
+
|
31
|
+
#if __x86_64__ || __i386__
|
32
|
+
#include <x86intrin.h>
|
33
|
+
#elif __aarch64__
|
34
|
+
#include <arm_neon.h>
|
35
|
+
#endif
|
36
|
+
|
37
|
+
#endif
|
38
|
+
|
39
|
+
//
|
40
|
+
// NOTE(casey): Set #define's to their defaults
|
41
|
+
//
|
42
|
+
|
43
|
+
#if !defined(MEOW_HASH_INTEL) || !defined(MEOW_HASH_ARMV8)
|
44
|
+
#if __x86_64__ || _M_AMD64
|
45
|
+
#define MEOW_HASH_INTEL 1
|
46
|
+
#define MEOW_64BIT 1
|
47
|
+
#define MEOW_PAGESIZE 4096
|
48
|
+
#elif __i386__ || _M_IX86
|
49
|
+
#define MEOW_HASH_INTEL 1
|
50
|
+
#define MEOW_64BIT 0
|
51
|
+
#define MEOW_PAGESIZE 4096
|
52
|
+
#elif __aarch64__ || _M_ARM64
|
53
|
+
#define MEOW_HASH_ARMV8 1
|
54
|
+
#define MEOW_64BIT 1
|
55
|
+
#define MEOW_PAGESIZE 4096
|
56
|
+
#else
|
57
|
+
#error Cannot determine architecture to use!
|
58
|
+
#endif
|
59
|
+
#endif
|
60
|
+
|
61
|
+
//
|
62
|
+
// NOTE(casey): Define basic types
|
63
|
+
//
|
64
|
+
|
65
|
+
#define meow_u8 char unsigned
|
66
|
+
#define meow_u16 short unsigned
|
67
|
+
#define meow_u32 int unsigned
|
68
|
+
#define meow_u64 long long unsigned
|
69
|
+
|
70
|
+
#if MEOW_64BIT
|
71
|
+
#define meow_umm long long unsigned
|
72
|
+
#else
|
73
|
+
#define meow_umm int unsigned
|
74
|
+
#endif
|
75
|
+
|
76
|
+
//
|
77
|
+
// NOTE(casey): Operations for x64 processors
|
78
|
+
//
|
79
|
+
|
80
|
+
#if MEOW_HASH_INTEL
|
81
|
+
|
82
|
+
#define meow_u128 __m128i
|
83
|
+
#define meow_aes_128 __m128i
|
84
|
+
#define meow_u256 __m256i
|
85
|
+
#define meow_aes_256 __m256i
|
86
|
+
#define meow_u512 __m512i
|
87
|
+
#define meow_aes_512 __m512i
|
88
|
+
|
89
|
+
#define MeowU32From(A, I) (_mm_extract_epi32((A), (I)))
|
90
|
+
#define MeowU64From(A, I) (_mm_extract_epi64((A), (I)))
|
91
|
+
#define MeowHashesAreEqual(A, B) (_mm_movemask_epi8(_mm_cmpeq_epi8((A), (B))) == 0xFFFF)
|
92
|
+
|
93
|
+
#define Meow128_AESDEC(Prior, Xor) _mm_aesdec_si128((Prior), (Xor))
|
94
|
+
#define Meow128_AESDEC_Mem(Prior, Xor) _mm_aesdec_si128((Prior), _mm_loadu_si128((meow_u128 *)(Xor)))
|
95
|
+
#define Meow128_AESDEC_Finalize(A) (A)
|
96
|
+
#define Meow128_Set64x2(Low64, High64) _mm_set_epi64x((High64), (Low64))
|
97
|
+
#define Meow128_Set64x2_State(Low64, High64) Meow128_Set64x2(Low64, High64)
|
98
|
+
#define Meow128_GetAESConstant(Ptr) (*(meow_u128 *)(Ptr))
|
99
|
+
|
100
|
+
#define Meow128_And_Mem(A,B) _mm_and_si128((A),_mm_loadu_si128((meow_u128 *)(B)))
|
101
|
+
#define Meow128_Shuffle_Mem(Mem,Control) _mm_shuffle_epi8(_mm_loadu_si128((meow_u128 *)(Mem)),_mm_loadu_si128((meow_u128 *)(Control)))
|
102
|
+
|
103
|
+
// TODO(casey): Not sure if this should actually be Meow128_Zero(A) ((A) = _mm_setzero_si128()), maybe
|
104
|
+
#define Meow128_Zero() _mm_setzero_si128()
|
105
|
+
|
106
|
+
#define Meow256_AESDEC(Prior, XOr) _mm256_aesdec_epi128((Prior), (XOr))
|
107
|
+
#define Meow256_AESDEC_Mem(Prior, XOr) _mm256_aesdec_epi128((Prior), *(meow_u256 *)(XOr))
|
108
|
+
#define Meow256_Zero() _mm256_setzero_si256()
|
109
|
+
#define Meow256_PartialLoad(A, B) _mm256_mask_loadu_epi8(_mm256_setzero_si256(), _cvtu32_mask32((1UL<<(B)) - 1), (A))
|
110
|
+
#define Meow128_FromLow(A) _mm256_extracti128_si256((A), 0)
|
111
|
+
#define Meow128_FromHigh(A) _mm256_extracti128_si256((A), 1)
|
112
|
+
|
113
|
+
#define Meow512_AESDEC(Prior, XOr) _mm512_aesdec_epi128((Prior), (XOr))
|
114
|
+
#define Meow512_AESDEC_Mem(Prior, XOr) _mm512_aesdec_epi128((Prior), *(meow_u512 *)(XOr))
|
115
|
+
#define Meow512_Zero() _mm512_setzero_si512()
|
116
|
+
#define Meow512_PartialLoad(A, B) _mm512_mask_loadu_epi8(_mm512_setzero_si512(), _cvtu64_mask64((1ULL<<(B)) - 1), (A))
|
117
|
+
#define Meow256_FromLow(A) _mm512_extracti64x4_epi64((A), 0)
|
118
|
+
#define Meow256_FromHigh(A) _mm512_extracti64x4_epi64((A), 1)
|
119
|
+
|
120
|
+
//
|
121
|
+
// NOTE(casey): Operations for ARM processors
|
122
|
+
//
|
123
|
+
|
124
|
+
#elif MEOW_HASH_ARMV8
|
125
|
+
|
126
|
+
#define meow_u128 uint8x16_t
|
127
|
+
|
128
|
+
// NOTE(mmozeiko): AES opcodes on ARMv8 work a bit differently than on Intel
|
129
|
+
// On Intel the "x = AESDEC(x, m)" does following:
|
130
|
+
// x = InvMixColumns(SubBytes(ShiftRows(x))) ^ m
|
131
|
+
// But on ARMv8 the "x = AESDEC(x, m)" does following:
|
132
|
+
// x = SubBytes(ShiftRows(x ^ m))
|
133
|
+
// Thus on ARMv8 it requires extra InvMixColumns call and delay on Xor operation.
|
134
|
+
// On iteration N it needs to use m[N-1] as input, and remeber m[N] for next iteration.
|
135
|
+
// This structure will store memory operand in member B which will be used in
|
136
|
+
// next AESDEC opcode. Remember to do one more XOR(A,B) when finishing AES
|
137
|
+
// operations in a loop.
|
138
|
+
typedef struct {
|
139
|
+
meow_u128 A;
|
140
|
+
meow_u128 B;
|
141
|
+
} meow_aes_128;
|
142
|
+
|
143
|
+
#define MeowU32From(A, I) (vgetq_lane_u32(vreinterpretq_u32_u8((A)), (I)))
|
144
|
+
#define MeowU64From(A, I) (vgetq_lane_u64(vreinterpretq_u64_u8((A)), (I)))
|
145
|
+
|
146
|
+
static int
|
147
|
+
MeowHashesAreEqualImpl(meow_u128 A, meow_u128 B)
|
148
|
+
{
|
149
|
+
uint8x16_t Powers = {
|
150
|
+
1, 2, 4, 8, 16, 32, 64, 128, 1, 2, 4, 8, 16, 32, 64, 128,
|
151
|
+
};
|
152
|
+
|
153
|
+
uint8x16_t Input = vceqq_u8(A, B);
|
154
|
+
uint64x2_t Mask = vpaddlq_u32(vpaddlq_u16(vpaddlq_u8(vandq_u8(Input, Powers))));
|
155
|
+
|
156
|
+
meow_u16 Output;
|
157
|
+
vst1q_lane_u8((meow_u8*)&Output + 0, vreinterpretq_u8_u64(Mask), 0);
|
158
|
+
vst1q_lane_u8((meow_u8*)&Output + 1, vreinterpretq_u8_u64(Mask), 8);
|
159
|
+
return Output == 0xFFFF;
|
160
|
+
}
|
161
|
+
|
162
|
+
#define MeowHashesAreEqual(A, B) MeowHashesAreEqualImpl((A), (B))
|
163
|
+
|
164
|
+
static meow_aes_128
|
165
|
+
Meow128_AESDEC(meow_aes_128 Prior, meow_u128 Xor)
|
166
|
+
{
|
167
|
+
meow_aes_128 R;
|
168
|
+
R.A = vaesimcq_u8(vaesdq_u8(Prior.A, Prior.B));
|
169
|
+
R.B = Xor;
|
170
|
+
return(R);
|
171
|
+
}
|
172
|
+
|
173
|
+
static meow_aes_128
|
174
|
+
Meow128_AESDEC_Mem(meow_aes_128 Prior, void *Xor)
|
175
|
+
{
|
176
|
+
meow_aes_128 R;
|
177
|
+
R.A = vaesimcq_u8(vaesdq_u8(Prior.A, Prior.B));
|
178
|
+
R.B = vld1q_u8((meow_u8*)Xor);
|
179
|
+
return(R);
|
180
|
+
}
|
181
|
+
|
182
|
+
static meow_u128
|
183
|
+
Meow128_AESDEC_Finalize(meow_aes_128 Value)
|
184
|
+
{
|
185
|
+
meow_u128 R = veorq_u8(Value.A, Value.B);
|
186
|
+
return(R);
|
187
|
+
}
|
188
|
+
|
189
|
+
static meow_u128
|
190
|
+
Meow128_Zero()
|
191
|
+
{
|
192
|
+
meow_u128 R = vdupq_n_u8(0);
|
193
|
+
return(R);
|
194
|
+
}
|
195
|
+
|
196
|
+
static meow_aes_128
|
197
|
+
Meow128_GetAESConstant(const meow_u8 *Ptr)
|
198
|
+
{
|
199
|
+
meow_aes_128 R;
|
200
|
+
R.A = vld1q_u8(Ptr);
|
201
|
+
R.B = vdupq_n_u8(0);
|
202
|
+
return(R);
|
203
|
+
}
|
204
|
+
|
205
|
+
static meow_u128
|
206
|
+
Meow128_Set64x2(meow_u64 Low64, meow_u64 High64)
|
207
|
+
{
|
208
|
+
meow_u128 R = vreinterpretq_u8_u64(vcombine_u64(vcreate_u64(Low64), vcreate_u64(High64)));
|
209
|
+
return(R);
|
210
|
+
}
|
211
|
+
|
212
|
+
static meow_aes_128
|
213
|
+
Meow128_Set64x2_State(meow_u64 Low64, meow_u64 High64)
|
214
|
+
{
|
215
|
+
meow_aes_128 R;
|
216
|
+
R.A = Meow128_Set64x2(Low64, High64);
|
217
|
+
R.B = Meow128_Zero();
|
218
|
+
return(R);
|
219
|
+
}
|
220
|
+
|
221
|
+
#define Meow128_And_Mem(A,B) vandq_u8((A), vld1q_u8((meow_u8 *)B))
|
222
|
+
#define Meow128_Shuffle_Mem(Mem,Control) vqtbl1q_u8(vld1q_u8((meow_u8 *)(Mem)),vld1q_u8((meow_u8 *)(Control)))
|
223
|
+
|
224
|
+
#endif
|
225
|
+
|
226
|
+
#define MEOW_HASH_VERSION 4
|
227
|
+
#define MEOW_HASH_VERSION_NAME "0.4/himalayan"
|
228
|
+
|
229
|
+
#if MEOW_INCLUDE_C
|
230
|
+
|
231
|
+
// NOTE(casey): Unfortunately, if you want an ANSI-C version, we have to slow everyone
|
232
|
+
// else down because you can't return 128-bit values by register anymore (in case the
|
233
|
+
// CPU doesn't support that)
|
234
|
+
union meow_hash
|
235
|
+
{
|
236
|
+
meow_u128 u128;
|
237
|
+
meow_u64 u64[2];
|
238
|
+
meow_u32 u32[4];
|
239
|
+
};
|
240
|
+
#define Meow128_CopyToHash(A, B) ((B).u128 = (A))
|
241
|
+
|
242
|
+
#undef MeowU64From
|
243
|
+
#undef MeowU32From
|
244
|
+
#undef MeowHashesAreEqual
|
245
|
+
#define MeowU32From(A, I) ((A).u32[I])
|
246
|
+
#define MeowU64From(A, I) ((A).u64[I])
|
247
|
+
#define MeowHashesAreEqual(A, B) (((A).u32[0] == (B).u32[0]) && ((A).u32[1] == (B).u32[1]) && ((A).u32[2] == (B).u32[2]) && ((A).u32[3] == (B).u32[3]))
|
248
|
+
|
249
|
+
#else
|
250
|
+
|
251
|
+
typedef meow_u128 meow_hash;
|
252
|
+
#define Meow128_CopyToHash(A, B) ((B) = (A))
|
253
|
+
|
254
|
+
#endif
|
255
|
+
|
256
|
+
typedef struct meow_hash_state meow_hash_state;
|
257
|
+
typedef meow_hash meow_hash_implementation(meow_u64 Seed, meow_u64 Len, void *Source);
|
258
|
+
typedef void meow_absorb_implementation(struct meow_hash_state *State, meow_u64 Len, void *Source);
|
259
|
+
|
260
|
+
#define MEOW_HASH_INTRINSICS_H
|
261
|
+
#endif
|
@@ -0,0 +1,48 @@
|
|
1
|
+
#include <ruby.h>
|
2
|
+
|
3
|
+
#include "meow_intrinsics.h"
|
4
|
+
#include "meow_hash.h"
|
5
|
+
|
6
|
+
#define VERSION "0.4"
|
7
|
+
|
8
|
+
/*
|
9
|
+
*/
|
10
|
+
static VALUE
|
11
|
+
rb_meow_version(int argc, VALUE *argv, VALUE klass)
|
12
|
+
{
|
13
|
+
return rb_sprintf("v%s", VERSION);
|
14
|
+
}
|
15
|
+
|
16
|
+
static void
|
17
|
+
to_chars(unsigned long n, unsigned char *bytes)
|
18
|
+
{
|
19
|
+
bytes[0] = (n >> 24) & 0xFF;
|
20
|
+
bytes[1] = (n >> 16) & 0xFF;
|
21
|
+
bytes[2] = (n >> 8) & 0xFF;
|
22
|
+
bytes[3] = n & 0xFF;
|
23
|
+
}
|
24
|
+
|
25
|
+
static VALUE
|
26
|
+
rb_meow_hash(VALUE self, VALUE toHash)
|
27
|
+
{
|
28
|
+
Check_Type(toHash, T_STRING);
|
29
|
+
|
30
|
+
meow_hash Hash = MeowHash_Accelerated(0, RSTRING_LEN(toHash), StringValuePtr(toHash));
|
31
|
+
unsigned char buffer[16];
|
32
|
+
to_chars(MeowU32From(Hash, 3), &buffer[0]);
|
33
|
+
to_chars(MeowU32From(Hash, 2), &buffer[4]);
|
34
|
+
to_chars(MeowU32From(Hash, 1), &buffer[8]);
|
35
|
+
to_chars(MeowU32From(Hash, 0), &buffer[12]);
|
36
|
+
VALUE rb_binary = rb_str_new(buffer, 16);
|
37
|
+
return rb_binary;
|
38
|
+
}
|
39
|
+
|
40
|
+
void
|
41
|
+
Init_meowhash()
|
42
|
+
{
|
43
|
+
VALUE mMH = rb_define_module("MeowHash");
|
44
|
+
VALUE cHasher = rb_define_module_under(mMH, "Hasher");
|
45
|
+
|
46
|
+
rb_define_module_function(cHasher, "version", rb_meow_version, 0);
|
47
|
+
rb_define_module_function(cHasher, "digest", rb_meow_hash, 1);
|
48
|
+
}
|
data/lib/meowhash.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'meowhash/meowhash'
|
data/meowhash.gemspec
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'meowhash'
|
3
|
+
s.version = '0.4.0'
|
4
|
+
s.summary = 'Meow Hash for Ruby'
|
5
|
+
s.description = <<-DOC
|
6
|
+
Meow Hash C extension for Ruby
|
7
|
+
DOC
|
8
|
+
s.homepage = 'https://github.com/alexsnaps/meowhash'
|
9
|
+
s.authors = ['Alex Snaps']
|
10
|
+
s.email = ['alex.snaps@gmail.com']
|
11
|
+
s.license = 'zlib'
|
12
|
+
|
13
|
+
s.files = `git ls-files`.split("\n")
|
14
|
+
s.extensions = ['ext/meowhash/extconf.rb']
|
15
|
+
s.add_development_dependency 'rake-compiler', '~> 1.0'
|
16
|
+
s.add_development_dependency 'test-unit'
|
17
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'meowhash'
|
3
|
+
|
4
|
+
class TestMeowHash < Test::Unit::TestCase
|
5
|
+
def test_version
|
6
|
+
assert MeowHash::Hasher.version() == "v0.4"
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_hasher
|
10
|
+
assert_equal("0591045C3C3F33B515D7D2E366FBF082".downcase, MeowHash::Hasher.digest("toto").each_byte.map { |b| b.to_s(16).rjust(2,'0') }.join)
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_raises_on_non_string
|
14
|
+
assert_raise TypeError do
|
15
|
+
MeowHash::Hasher.digest(42)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_meow_hash_version
|
20
|
+
file = File.open("ext/meowhash/meow_hash.h", "rb")
|
21
|
+
assert_equal("9F177F35D31536CDE904FF01EE0E9A72".downcase, MeowHash::Hasher.digest(file.read).each_byte.map { |b| b.to_s(16).rjust(2,'0') }.join)
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_meow_intrinsics_version
|
25
|
+
file = File.open("ext/meowhash/meow_intrinsics.h", "rb")
|
26
|
+
assert_equal("B6F0D59805A037BFFC13AF959E08CC92".downcase, MeowHash::Hasher.digest(file.read).each_byte.map { |b| b.to_s(16).rjust(2,'0') }.join)
|
27
|
+
end
|
28
|
+
end
|
metadata
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: meowhash
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.4.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Alex Snaps
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-11-23 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake-compiler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: test-unit
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: " Meow Hash C extension for Ruby\n"
|
42
|
+
email:
|
43
|
+
- alex.snaps@gmail.com
|
44
|
+
executables: []
|
45
|
+
extensions:
|
46
|
+
- ext/meowhash/extconf.rb
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- Gemfile
|
50
|
+
- LICENSE
|
51
|
+
- Rakefile
|
52
|
+
- ext/meowhash/extconf.rb
|
53
|
+
- ext/meowhash/meow_hash.h
|
54
|
+
- ext/meowhash/meow_intrinsics.h
|
55
|
+
- ext/meowhash/meowhash.c
|
56
|
+
- lib/meowhash.rb
|
57
|
+
- meowhash.gemspec
|
58
|
+
- test/test_meowhash.rb
|
59
|
+
homepage: https://github.com/alexsnaps/meowhash
|
60
|
+
licenses:
|
61
|
+
- zlib
|
62
|
+
metadata: {}
|
63
|
+
post_install_message:
|
64
|
+
rdoc_options: []
|
65
|
+
require_paths:
|
66
|
+
- lib
|
67
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: '0'
|
72
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
requirements: []
|
78
|
+
rubyforge_project:
|
79
|
+
rubygems_version: 2.7.6
|
80
|
+
signing_key:
|
81
|
+
specification_version: 4
|
82
|
+
summary: Meow Hash for Ruby
|
83
|
+
test_files: []
|