Skip to content

Commit

Permalink
Address review comments, add more tests, fixup siphash
Browse files Browse the repository at this point in the history
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
  • Loading branch information
jappeace committed Oct 6, 2021
1 parent e656df8 commit 4d3fa6a
Show file tree
Hide file tree
Showing 9 changed files with 423 additions and 381 deletions.
331 changes: 112 additions & 219 deletions cbits/siphash.c
Original file line number Diff line number Diff line change
@@ -1,262 +1,155 @@
/* Almost a verbatim copy of the reference implementation. */

#include <stddef.h>
#include "siphash.h"
#include <stddef.h>

#define ROTL(x,b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b))))

#define SIPROUND \
do { \
v0 += v1; v1=ROTL(v1,13); v1 ^= v0; v0=ROTL(v0,32); \
v2 += v3; v3=ROTL(v3,16); v3 ^= v2; \
v0 += v3; v3=ROTL(v3,21); v3 ^= v0; \
v2 += v1; v1=ROTL(v1,17); v1 ^= v2; v2=ROTL(v2,32); \
} while(0)
#define ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b))))

#define SIPROUND \
do { \
v[0] += v[1]; \
v[1] = ROTL(v[1], 13); \
v[1] ^= v[0]; \
v[0] = ROTL(v[0], 32); \
v[2] += v[3]; \
v[3] = ROTL(v[3], 16); \
v[3] ^= v[2]; \
v[0] += v[3]; \
v[3] = ROTL(v[3], 21); \
v[3] ^= v[0]; \
v[2] += v[1]; \
v[1] = ROTL(v[1], 17); \
v[1] ^= v[2]; \
v[2] = ROTL(v[2], 32); \
} while (0)

#if defined(__i386)
# define _siphash24 plain_siphash24
#define _siphash24 plain_siphash24
#endif

static inline uint64_t odd_read(const u8 *p, int count, uint64_t val, int shift)
{
switch (count) {
case 7: val |= ((uint64_t)p[6]) << (shift + 48);
case 6: val |= ((uint64_t)p[5]) << (shift + 40);
case 5: val |= ((uint64_t)p[4]) << (shift + 32);
case 4: val |= ((uint64_t)p[3]) << (shift + 24);
case 3: val |= ((uint64_t)p[2]) << (shift + 16);
case 2: val |= ((uint64_t)p[1]) << (shift + 8);
case 1: val |= ((uint64_t)p[0]) << shift;
}
return val;
static inline uint64_t odd_read(const u8 *p, int count, uint64_t val,
int shift) {
switch (count) {
case 7:
val |= ((uint64_t)p[6]) << (shift + 48);
case 6:
val |= ((uint64_t)p[5]) << (shift + 40);
case 5:
val |= ((uint64_t)p[4]) << (shift + 32);
case 4:
val |= ((uint64_t)p[3]) << (shift + 24);
case 3:
val |= ((uint64_t)p[2]) << (shift + 16);
case 2:
val |= ((uint64_t)p[1]) << (shift + 8);
case 1:
val |= ((uint64_t)p[0]) << shift;
}
return val;
}

static inline uint64_t _siphash(int c, int d, uint64_t k0, uint64_t k1,
const u8 *str, size_t len)
{
uint64_t v0 = 0x736f6d6570736575ull ^ k0;
uint64_t v1 = 0x646f72616e646f6dull ^ k1;
uint64_t v2 = 0x6c7967656e657261ull ^ k0;
uint64_t v3 = 0x7465646279746573ull ^ k1;
const u8 *end, *p;
uint64_t b;
int i;

for (p = str, end = str + (len & ~7); p < end; p += 8) {
uint64_t m = peek_uint64_tle((uint64_t *) p);
v3 ^= m;
if (c == 2) {
SIPROUND;
SIPROUND;
} else {
for (i = 0; i < c; i++)
SIPROUND;
}
v0 ^= m;
static inline void _siphash_compression
( const int c
, uint64_t v[4] // this mutates, allowing you to keep on hashing
, const u8 *str
, const size_t len
){
const u8 *p;
const u8* end;

// compress message
for (p = str, end = str + (len & ~7); p < end; p += 8) {
uint64_t m = peek_uint64_tle((uint64_t *)p);
v[3] ^= m;
for (int i = 0; i < c; i++){
SIPROUND;
}
v[0] ^= m;
}

b = odd_read(p, len & 7, ((uint64_t) len) << 56, 0);
// compress remainder
uint64_t b = odd_read(p, len & 7, ((uint64_t)len) << 56, 0);

v3 ^= b;
if (c == 2) {
SIPROUND;
SIPROUND;
} else {
for (i = 0; i < c; i++)
SIPROUND;
}
v0 ^= b;

v2 ^= 0xff;
if (d == 4) {
SIPROUND;
SIPROUND;
SIPROUND;
SIPROUND;
} else {
for (i = 0; i < d; i++)
SIPROUND;
}
b = v0 ^ v1 ^ v2 ^ v3;
return b;
v[3] ^= b;
for (int i = 0; i < c; i++){
SIPROUND;
}
v[0] ^= b;
}


static inline uint64_t _siphash24(uint64_t k0, uint64_t k1, const u8 *str, size_t len)
{
return _siphash(2, 4, k0, k1, str, len);
static inline uint64_t _siphash_finalize
( const int d
, uint64_t v[4] // this mutates, allowing you to keep on hashing
){
v[2] ^= 0xff;
if (d == 4) {
SIPROUND;
SIPROUND;
SIPROUND;
SIPROUND;
} else {
for (int i = 0; i < d; i++)
SIPROUND;
}
return v[0] ^ v[1] ^ v[2] ^ v[3];
}

#if defined(__i386)
# undef _siphash24
#undef _siphash24

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

static void maybe_use_sse()
__attribute__((constructor));
static void maybe_use_sse() __attribute__((constructor));

static void maybe_use_sse()
{
uint32_t eax = 1, ebx, ecx, edx;
static void maybe_use_sse() {
uint32_t eax = 1, ebx, ecx, edx;

__asm volatile
("mov %%ebx, %%edi;" /* 32bit PIC: don't clobber ebx */
"cpuid;"
"mov %%ebx, %%esi;"
"mov %%edi, %%ebx;"
:"+a" (eax), "=S" (ebx), "=c" (ecx), "=d" (edx)
: :"edi");
__asm volatile("mov %%ebx, %%edi;" /* 32bit PIC: don't clobber ebx */
"cpuid;"
"mov %%ebx, %%esi;"
"mov %%edi, %%ebx;"
: "+a"(eax), "=S"(ebx), "=c"(ecx), "=d"(edx)
:
: "edi");

#if defined(HAVE_SSE2)
if (edx & (1 << 26))
_siphash24 = hashable_siphash24_sse2;
if (edx & (1 << 26))
_siphash24 = hashable_siphash24_sse2;
#if defined(HAVE_SSE41)
else if (ecx & (1 << 19))
_siphash24 = hashable_siphash24_sse41;
else if (ecx & (1 << 19))
_siphash24 = hashable_siphash24_sse41;
#endif
else
else
#endif
_siphash24 = plain_siphash24;
_siphash24 = plain_siphash24;
}

#endif

/* ghci's linker fails to call static initializers. */
static inline void ensure_sse_init()
{
static inline void ensure_sse_init() {
#if defined(__i386)
if (_siphash24 == NULL)
maybe_use_sse();
if (_siphash24 == NULL)
maybe_use_sse();
#endif
}

uint64_t hashable_siphash(int c, int d, uint64_t k0, uint64_t k1, const u8 *str, size_t len)
{
return _siphash(c, d, k0, k1, str, len);
}

uint64_t hashable_siphash24(uint64_t k0, uint64_t k1, const u8 *str, size_t len)
{
ensure_sse_init();
return _siphash24(k0, k1, str, len);
}

/* Used for ByteArray#s. We can't treat them like pointers in
native Haskell, but we can in unsafe FFI calls.
*/
uint64_t hashable_siphash24_offset(uint64_t k0, uint64_t k1,
const u8 *str, size_t off, size_t len)
{
ensure_sse_init();
return _siphash24(k0, k1, str + off, len);
}

static int _siphash_chunk(int c, int d, int buffered, uint64_t v[5],
const u8 *str, size_t len, size_t totallen)
{
uint64_t v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3], m, b;
const u8 *p, *end;
uint64_t carry = 0;
int i;

if (buffered > 0) {
int unbuffered = 8 - buffered;
int tobuffer = unbuffered > len ? len : unbuffered;
int shift = buffered << 3;

m = odd_read(str, tobuffer, v[4], shift);
str += tobuffer;
buffered += tobuffer;
len -= tobuffer;

if (buffered < 8)
carry = m;
else {
v3 ^= m;
if (c == 2) {
SIPROUND;
SIPROUND;
} else {
for (i = 0; i < c; i++)
SIPROUND;
}
v0 ^= m;
buffered = 0;
m = 0;
}
}

for (p = str, end = str + (len & ~7); p < end; p += 8) {
m = peek_uint64_tle((uint64_t *) p);
v3 ^= m;
if (c == 2) {
SIPROUND;
SIPROUND;
} else {
for (i = 0; i < c; i++)
SIPROUND;
}
v0 ^= m;
}

b = odd_read(p, len & 7, 0, 0);

if (totallen == -1) {
v[0] = v0;
v[1] = v1;
v[2] = v2;
v[3] = v3;
v[4] = b | carry;

return buffered + (len & 7);
}

b |= ((uint64_t) totallen) << 56;

v3 ^= b;
if (c == 2) {
SIPROUND;
SIPROUND;
} else {
for (i = 0; i < c; i++)
SIPROUND;
}
v0 ^= b;

v2 ^= 0xff;
if (d == 4) {
SIPROUND;
SIPROUND;
SIPROUND;
SIPROUND;
} else {
for (i = 0; i < d; i++)
SIPROUND;
}
v[4] = v0 ^ v1 ^ v2 ^ v3;
return 0;
}

void hashable_siphash_init(uint64_t k0, uint64_t k1, uint64_t *v)
{
v[0] = 0x736f6d6570736575ull ^ k0;
v[1] = 0x646f72616e646f6dull ^ k1;
v[2] = 0x6c7967656e657261ull ^ k0;
v[3] = 0x7465646279746573ull ^ k1;
v[4] = 0;
}

int hashable_siphash24_chunk(int buffered, uint64_t v[5], const u8 *str,
size_t len, size_t totallen)
{
return _siphash_chunk(2, 4, buffered, v, str, len, totallen);
void hashable_siphash_init(uint64_t k0, uint64_t k1, uint64_t *v) {
ensure_sse_init();
v[0] = 0x736f6d6570736575ull ^ k0;
v[1] = 0x646f72616e646f6dull ^ k1;
v[2] = 0x6c7967656e657261ull ^ k0;
v[3] = 0x7465646279746573ull ^ k1;
}

/*
* Used for ByteArray#.
*/
int hashable_siphash24_chunk_offset(int buffered, uint64_t v[5], const u8 *str,
size_t off, size_t len, size_t totallen)
{
return _siphash_chunk(2, 4, buffered, v, str + off, len, totallen);
void hashable_siphash_compression(const int c, uint64_t v[4], const u8 *str,
size_t off, size_t len) {
_siphash_compression(c, v, str + off, len);
}

uint64_t hashable_siphash_finalize(const int d, uint64_t *v) {
return _siphash_finalize(d, v);
}
Loading

0 comments on commit 4d3fa6a

Please sign in to comment.