#define PERL_NO_GET_CONTEXT /* we want efficiency */ #include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include "ppport.h" #include "fix_inline.h" #include <stdlib.h> #include <sys/types.h> /* This is SipHash by Jean-Philippe Aumasson and Daniel J. Bernstein. * The authors claim it is relatively secure compared to the alternatives * and that performance wise it is a suitable hash for languages like Perl. * See: * * https://www.131002.net/siphash/ * * This implementation seems to perform slightly slower than one-at-a-time for * short keys, but degrades slower for longer keys. Murmur Hash outperforms it * regardless of keys size. * * It is 64 bit only. */ /* Find best way to ROTL32/ROTL64 */ #ifndef ROTL64 #if defined(_MSC_VER) #include <stdlib.h> /* Microsoft put _rotl declaration in here */ #define ROTL64(x,r) _rotl64(x,r) #else /* gcc recognises this code and generates a rotate instruction for CPUs with one */ #define ROTL64(x,r) (((uint64_t)x << r) | ((uint64_t)x >> (64 - r))) #endif #endif #ifndef U8TO64_LE #define U8TO64_LE(p) \ (((uint64_t)((p)[0]) ) | \ ((uint64_t)((p)[1]) << 8) | \ ((uint64_t)((p)[2]) << 16) | \ ((uint64_t)((p)[3]) << 24) | \ ((uint64_t)((p)[4]) << 32) | \ ((uint64_t)((p)[5]) << 40) | \ ((uint64_t)((p)[6]) << 48) | \ ((uint64_t)((p)[7]) << 56)) #endif #define SIPROUND \ do { \ v0 += v1; v1=ROTL64(v1,13); v1 ^= v0; v0=ROTL64(v0,32); \ v2 += v3; v3=ROTL64(v3,16); v3 ^= v2; \ v0 += v3; v3=ROTL64(v3,21); v3 ^= v0; \ v2 += v1; v1=ROTL64(v1,17); v1 ^= v2; v2=ROTL64(v2,32); \ } while(0) /* SipHash-2-4 */ PERL_STATIC_INLINE uint64_t siphash_2_4_from_perl(const unsigned char *in, const STRLEN inlen) { /* "somepseudorandomlygeneratedbytes" */ uint64_t v0 = (uint64_t)(0x736f6d6570736575); uint64_t v1 = (uint64_t)(0x646f72616e646f6d); uint64_t v2 = (uint64_t)(0x6c7967656e657261); uint64_t v3 = (uint64_t)(0x7465646279746573); uint64_t b; uint64_t k0 = 0; uint64_t k1 = 0; uint64_t m; const int left = inlen & 7; const U8 *end = in + inlen - left; b = ( ( uint64_t )(inlen) ) << 56; v3 ^= k1; v2 ^= k0; v1 ^= k1; v0 ^= k0; for ( ; in != end; in += 8 ) { m = U8TO64_LE( in ); v3 ^= m; SIPROUND; SIPROUND; v0 ^= m; } switch( left ) { case 7: b |= ( ( uint64_t )in[ 6] ) << 48; case 6: b |= ( ( uint64_t )in[ 5] ) << 40; case 5: b |= ( ( uint64_t )in[ 4] ) << 32; case 4: b |= ( ( uint64_t )in[ 3] ) << 24; case 3: b |= ( ( uint64_t )in[ 2] ) << 16; case 2: b |= ( ( uint64_t )in[ 1] ) << 8; case 1: b |= ( ( uint64_t )in[ 0] ); break; case 0: break; } v3 ^= b; SIPROUND; SIPROUND; v0 ^= b; v2 ^= 0xff; SIPROUND; SIPROUND; SIPROUND; SIPROUND; b = v0 ^ v1 ^ v2 ^ v3; return b; /*return (U32)(b & U32_MAX);*/ } int32_t JumpConsistentHash(uint64_t key, int32_t num_buckets) { int64_t b = 1; int64_t j = 0; while (j < num_buckets) { b = j; key = key * 2862933555777941757ULL + 1; j = (b + 1) * ((double)(1LL << 31) / (double)((key >> 33) + 1)); } return b; } MODULE = Algorithm::ConsistentHash::JumpHash PACKAGE = Algorithm::ConsistentHash::JumpHash PROTOTYPES: DISABLE int32_t jumphash_numeric(uint64_t key, int32_t num_buckets) CODE: RETVAL = JumpConsistentHash(key, num_buckets); OUTPUT: RETVAL int32_t jumphash_siphash(SV *str, uint64_t num_buckets) CODE: { STRLEN len; /* FIXME */ const char * strval = SvPVbyte(str, len); const uint64_t hashval = siphash_2_4_from_perl(strval, len); RETVAL = JumpConsistentHash(hashval, num_buckets); } OUTPUT: RETVAL