meowhash 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|