Skip to content

Commit 4d3fa6a

Browse files
committed
Address review comments, add more tests, fixup siphash
this is just the old implementation of siphash. with some tweaks to make it more streamable (still not great but good enough for benchmarks). -- squash log Use capi if available for offset Add more capi calls Flip expectation and actual args it showed up wrongly in the test suite Add more capi, add TODO Revert Hashable/Class to upstream master trying to contain changes to just that module Fix regression (once more) Don't expose the old functions Fix capi issues. Apprantly half of these functions weren't defined in the header. Tell people to use sse instead of asking Apply clang format Add siphash to benchmarks Add initializeState function, this will capture initizlie don't know how to do finalize yet Make it compile again (apparntly k1 and salt were used interchanbly) that's not a good idea Clean out the c code a bit. It was really strange. Make it build.. again? re-add header file I guess this will just keep on bein inconsistent remove cbits *.h from benchamark cabal file OMG that worked, lazy text now bytearray man, I doubted myself every step along the way, maybe I can do this? Draft for doing bytestring Make some headway on the regession tests Make testsuite pass once more Put in rounds as an argument Add compression round as args Add some common sense tests on previous state fails of course, not sure what's happening here Import <$> I guess different chars can also produce same hashes. Cleanup C-api for a bit it's still utterly broken however. I guess I need to try it out in C and see why the state isn't changing. Chunck -> Chunk Get rid of more c code idk maybe this do something with the sse stuff (after reading ghci comment) Figured out why no compression (lol) Update 64bit text once more Better explain 0 Split lines Lazy text can be big as well don't funroll by hand, add comments explaing what's going on Use properties instead of hardcoded test cases for statefullness Add prefix based tests as well hmm not so ez for text this also makes me doubt the bytestring implementaiton. I should check if with larger arbitrary instances that doesn't fall over (text tends to have larger bytestring representations then bytestring) Guess we really did solve the bytestring implementation This should pass CI
1 parent e656df8 commit 4d3fa6a

File tree

9 files changed

+423
-381
lines changed

9 files changed

+423
-381
lines changed

cbits/siphash.c

+112-219
Original file line numberDiff line numberDiff line change
@@ -1,262 +1,155 @@
11
/* Almost a verbatim copy of the reference implementation. */
22

3-
#include <stddef.h>
43
#include "siphash.h"
4+
#include <stddef.h>
55

6-
#define ROTL(x,b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b))))
7-
8-
#define SIPROUND \
9-
do { \
10-
v0 += v1; v1=ROTL(v1,13); v1 ^= v0; v0=ROTL(v0,32); \
11-
v2 += v3; v3=ROTL(v3,16); v3 ^= v2; \
12-
v0 += v3; v3=ROTL(v3,21); v3 ^= v0; \
13-
v2 += v1; v1=ROTL(v1,17); v1 ^= v2; v2=ROTL(v2,32); \
14-
} while(0)
6+
#define ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b))))
7+
8+
#define SIPROUND \
9+
do { \
10+
v[0] += v[1]; \
11+
v[1] = ROTL(v[1], 13); \
12+
v[1] ^= v[0]; \
13+
v[0] = ROTL(v[0], 32); \
14+
v[2] += v[3]; \
15+
v[3] = ROTL(v[3], 16); \
16+
v[3] ^= v[2]; \
17+
v[0] += v[3]; \
18+
v[3] = ROTL(v[3], 21); \
19+
v[3] ^= v[0]; \
20+
v[2] += v[1]; \
21+
v[1] = ROTL(v[1], 17); \
22+
v[1] ^= v[2]; \
23+
v[2] = ROTL(v[2], 32); \
24+
} while (0)
1525

1626
#if defined(__i386)
17-
# define _siphash24 plain_siphash24
27+
#define _siphash24 plain_siphash24
1828
#endif
1929

20-
static inline uint64_t odd_read(const u8 *p, int count, uint64_t val, int shift)
21-
{
22-
switch (count) {
23-
case 7: val |= ((uint64_t)p[6]) << (shift + 48);
24-
case 6: val |= ((uint64_t)p[5]) << (shift + 40);
25-
case 5: val |= ((uint64_t)p[4]) << (shift + 32);
26-
case 4: val |= ((uint64_t)p[3]) << (shift + 24);
27-
case 3: val |= ((uint64_t)p[2]) << (shift + 16);
28-
case 2: val |= ((uint64_t)p[1]) << (shift + 8);
29-
case 1: val |= ((uint64_t)p[0]) << shift;
30-
}
31-
return val;
30+
static inline uint64_t odd_read(const u8 *p, int count, uint64_t val,
31+
int shift) {
32+
switch (count) {
33+
case 7:
34+
val |= ((uint64_t)p[6]) << (shift + 48);
35+
case 6:
36+
val |= ((uint64_t)p[5]) << (shift + 40);
37+
case 5:
38+
val |= ((uint64_t)p[4]) << (shift + 32);
39+
case 4:
40+
val |= ((uint64_t)p[3]) << (shift + 24);
41+
case 3:
42+
val |= ((uint64_t)p[2]) << (shift + 16);
43+
case 2:
44+
val |= ((uint64_t)p[1]) << (shift + 8);
45+
case 1:
46+
val |= ((uint64_t)p[0]) << shift;
47+
}
48+
return val;
3249
}
3350

34-
static inline uint64_t _siphash(int c, int d, uint64_t k0, uint64_t k1,
35-
const u8 *str, size_t len)
36-
{
37-
uint64_t v0 = 0x736f6d6570736575ull ^ k0;
38-
uint64_t v1 = 0x646f72616e646f6dull ^ k1;
39-
uint64_t v2 = 0x6c7967656e657261ull ^ k0;
40-
uint64_t v3 = 0x7465646279746573ull ^ k1;
41-
const u8 *end, *p;
42-
uint64_t b;
43-
int i;
44-
45-
for (p = str, end = str + (len & ~7); p < end; p += 8) {
46-
uint64_t m = peek_uint64_tle((uint64_t *) p);
47-
v3 ^= m;
48-
if (c == 2) {
49-
SIPROUND;
50-
SIPROUND;
51-
} else {
52-
for (i = 0; i < c; i++)
53-
SIPROUND;
54-
}
55-
v0 ^= m;
51+
static inline void _siphash_compression
52+
( const int c
53+
, uint64_t v[4] // this mutates, allowing you to keep on hashing
54+
, const u8 *str
55+
, const size_t len
56+
){
57+
const u8 *p;
58+
const u8* end;
59+
60+
// compress message
61+
for (p = str, end = str + (len & ~7); p < end; p += 8) {
62+
uint64_t m = peek_uint64_tle((uint64_t *)p);
63+
v[3] ^= m;
64+
for (int i = 0; i < c; i++){
65+
SIPROUND;
5666
}
67+
v[0] ^= m;
68+
}
5769

58-
b = odd_read(p, len & 7, ((uint64_t) len) << 56, 0);
70+
// compress remainder
71+
uint64_t b = odd_read(p, len & 7, ((uint64_t)len) << 56, 0);
5972

60-
v3 ^= b;
61-
if (c == 2) {
62-
SIPROUND;
63-
SIPROUND;
64-
} else {
65-
for (i = 0; i < c; i++)
66-
SIPROUND;
67-
}
68-
v0 ^= b;
69-
70-
v2 ^= 0xff;
71-
if (d == 4) {
72-
SIPROUND;
73-
SIPROUND;
74-
SIPROUND;
75-
SIPROUND;
76-
} else {
77-
for (i = 0; i < d; i++)
78-
SIPROUND;
79-
}
80-
b = v0 ^ v1 ^ v2 ^ v3;
81-
return b;
73+
v[3] ^= b;
74+
for (int i = 0; i < c; i++){
75+
SIPROUND;
76+
}
77+
v[0] ^= b;
8278
}
8379

84-
85-
static inline uint64_t _siphash24(uint64_t k0, uint64_t k1, const u8 *str, size_t len)
86-
{
87-
return _siphash(2, 4, k0, k1, str, len);
80+
static inline uint64_t _siphash_finalize
81+
( const int d
82+
, uint64_t v[4] // this mutates, allowing you to keep on hashing
83+
){
84+
v[2] ^= 0xff;
85+
if (d == 4) {
86+
SIPROUND;
87+
SIPROUND;
88+
SIPROUND;
89+
SIPROUND;
90+
} else {
91+
for (int i = 0; i < d; i++)
92+
SIPROUND;
93+
}
94+
return v[0] ^ v[1] ^ v[2] ^ v[3];
8895
}
8996

9097
#if defined(__i386)
91-
# undef _siphash24
98+
#undef _siphash24
9299

93100
static uint64_t (*_siphash24)(uint64_t k0, uint64_t k1, const u8 *, size_t);
94101

95-
static void maybe_use_sse()
96-
__attribute__((constructor));
102+
static void maybe_use_sse() __attribute__((constructor));
97103

98-
static void maybe_use_sse()
99-
{
100-
uint32_t eax = 1, ebx, ecx, edx;
104+
static void maybe_use_sse() {
105+
uint32_t eax = 1, ebx, ecx, edx;
101106

102-
__asm volatile
103-
("mov %%ebx, %%edi;" /* 32bit PIC: don't clobber ebx */
104-
"cpuid;"
105-
"mov %%ebx, %%esi;"
106-
"mov %%edi, %%ebx;"
107-
:"+a" (eax), "=S" (ebx), "=c" (ecx), "=d" (edx)
108-
: :"edi");
107+
__asm volatile("mov %%ebx, %%edi;" /* 32bit PIC: don't clobber ebx */
108+
"cpuid;"
109+
"mov %%ebx, %%esi;"
110+
"mov %%edi, %%ebx;"
111+
: "+a"(eax), "=S"(ebx), "=c"(ecx), "=d"(edx)
112+
:
113+
: "edi");
109114

110115
#if defined(HAVE_SSE2)
111-
if (edx & (1 << 26))
112-
_siphash24 = hashable_siphash24_sse2;
116+
if (edx & (1 << 26))
117+
_siphash24 = hashable_siphash24_sse2;
113118
#if defined(HAVE_SSE41)
114-
else if (ecx & (1 << 19))
115-
_siphash24 = hashable_siphash24_sse41;
119+
else if (ecx & (1 << 19))
120+
_siphash24 = hashable_siphash24_sse41;
116121
#endif
117-
else
122+
else
118123
#endif
119-
_siphash24 = plain_siphash24;
124+
_siphash24 = plain_siphash24;
120125
}
121126

122127
#endif
123128

124129
/* ghci's linker fails to call static initializers. */
125-
static inline void ensure_sse_init()
126-
{
130+
static inline void ensure_sse_init() {
127131
#if defined(__i386)
128-
if (_siphash24 == NULL)
129-
maybe_use_sse();
132+
if (_siphash24 == NULL)
133+
maybe_use_sse();
130134
#endif
131135
}
132136

133-
uint64_t hashable_siphash(int c, int d, uint64_t k0, uint64_t k1, const u8 *str, size_t len)
134-
{
135-
return _siphash(c, d, k0, k1, str, len);
136-
}
137-
138-
uint64_t hashable_siphash24(uint64_t k0, uint64_t k1, const u8 *str, size_t len)
139-
{
140-
ensure_sse_init();
141-
return _siphash24(k0, k1, str, len);
142-
}
143-
144-
/* Used for ByteArray#s. We can't treat them like pointers in
145-
native Haskell, but we can in unsafe FFI calls.
146-
*/
147-
uint64_t hashable_siphash24_offset(uint64_t k0, uint64_t k1,
148-
const u8 *str, size_t off, size_t len)
149-
{
150-
ensure_sse_init();
151-
return _siphash24(k0, k1, str + off, len);
152-
}
153-
154-
static int _siphash_chunk(int c, int d, int buffered, uint64_t v[5],
155-
const u8 *str, size_t len, size_t totallen)
156-
{
157-
uint64_t v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3], m, b;
158-
const u8 *p, *end;
159-
uint64_t carry = 0;
160-
int i;
161-
162-
if (buffered > 0) {
163-
int unbuffered = 8 - buffered;
164-
int tobuffer = unbuffered > len ? len : unbuffered;
165-
int shift = buffered << 3;
166-
167-
m = odd_read(str, tobuffer, v[4], shift);
168-
str += tobuffer;
169-
buffered += tobuffer;
170-
len -= tobuffer;
171-
172-
if (buffered < 8)
173-
carry = m;
174-
else {
175-
v3 ^= m;
176-
if (c == 2) {
177-
SIPROUND;
178-
SIPROUND;
179-
} else {
180-
for (i = 0; i < c; i++)
181-
SIPROUND;
182-
}
183-
v0 ^= m;
184-
buffered = 0;
185-
m = 0;
186-
}
187-
}
188-
189-
for (p = str, end = str + (len & ~7); p < end; p += 8) {
190-
m = peek_uint64_tle((uint64_t *) p);
191-
v3 ^= m;
192-
if (c == 2) {
193-
SIPROUND;
194-
SIPROUND;
195-
} else {
196-
for (i = 0; i < c; i++)
197-
SIPROUND;
198-
}
199-
v0 ^= m;
200-
}
201-
202-
b = odd_read(p, len & 7, 0, 0);
203-
204-
if (totallen == -1) {
205-
v[0] = v0;
206-
v[1] = v1;
207-
v[2] = v2;
208-
v[3] = v3;
209-
v[4] = b | carry;
210-
211-
return buffered + (len & 7);
212-
}
213-
214-
b |= ((uint64_t) totallen) << 56;
215-
216-
v3 ^= b;
217-
if (c == 2) {
218-
SIPROUND;
219-
SIPROUND;
220-
} else {
221-
for (i = 0; i < c; i++)
222-
SIPROUND;
223-
}
224-
v0 ^= b;
225-
226-
v2 ^= 0xff;
227-
if (d == 4) {
228-
SIPROUND;
229-
SIPROUND;
230-
SIPROUND;
231-
SIPROUND;
232-
} else {
233-
for (i = 0; i < d; i++)
234-
SIPROUND;
235-
}
236-
v[4] = v0 ^ v1 ^ v2 ^ v3;
237-
return 0;
238-
}
239-
240-
void hashable_siphash_init(uint64_t k0, uint64_t k1, uint64_t *v)
241-
{
242-
v[0] = 0x736f6d6570736575ull ^ k0;
243-
v[1] = 0x646f72616e646f6dull ^ k1;
244-
v[2] = 0x6c7967656e657261ull ^ k0;
245-
v[3] = 0x7465646279746573ull ^ k1;
246-
v[4] = 0;
247-
}
248-
249-
int hashable_siphash24_chunk(int buffered, uint64_t v[5], const u8 *str,
250-
size_t len, size_t totallen)
251-
{
252-
return _siphash_chunk(2, 4, buffered, v, str, len, totallen);
137+
void hashable_siphash_init(uint64_t k0, uint64_t k1, uint64_t *v) {
138+
ensure_sse_init();
139+
v[0] = 0x736f6d6570736575ull ^ k0;
140+
v[1] = 0x646f72616e646f6dull ^ k1;
141+
v[2] = 0x6c7967656e657261ull ^ k0;
142+
v[3] = 0x7465646279746573ull ^ k1;
253143
}
254144

255145
/*
256146
* Used for ByteArray#.
257147
*/
258-
int hashable_siphash24_chunk_offset(int buffered, uint64_t v[5], const u8 *str,
259-
size_t off, size_t len, size_t totallen)
260-
{
261-
return _siphash_chunk(2, 4, buffered, v, str + off, len, totallen);
148+
void hashable_siphash_compression(const int c, uint64_t v[4], const u8 *str,
149+
size_t off, size_t len) {
150+
_siphash_compression(c, v, str + off, len);
151+
}
152+
153+
uint64_t hashable_siphash_finalize(const int d, uint64_t *v) {
154+
return _siphash_finalize(d, v);
262155
}

0 commit comments

Comments
 (0)