diff --git a/src/utils/hash.c b/src/utils/hash.c index 59bc76d0..438f7319 100644 --- a/src/utils/hash.c +++ b/src/utils/hash.c @@ -29,6 +29,7 @@ NOINLINE u64 bqn_hashArr(B x, const u64 secret[4]) { // TODO manual separation o bytes = xia*sizeof(B); break; } + assert(bytes!=0); u64 r = wyhash(data, bytes, shHash, secret); if (isTemp) TFREE(data); dec(x); diff --git a/src/utils/wyhash.h b/src/utils/wyhash.h index f085bfbb..0e0cd81a 100644 --- a/src/utils/wyhash.h +++ b/src/utils/wyhash.h @@ -113,28 +113,39 @@ static inline uint64_t _wyr4(const uint8_t *p) { return (((v >> 24) & 0xff)| ((v >> 8) & 0xff00)| ((v << 8) & 0xff0000)| ((v << 24) & 0xff000000)); } #endif -static inline uint64_t _wyr3(const uint8_t *p, size_t k) { return (((uint64_t)p[0])<<16)|(((uint64_t)p[k>>1])<<8)|p[k-1];} -//wyhash main function + +#ifdef __BMI2__ +#include +#endif + +//wyhash main function; assumes len>0 and that it can read past the end of the input FORCE_INLINE uint64_t wyhash(const void *key, size_t len, uint64_t seed, const uint64_t *secret){ - const uint8_t *p=(const uint8_t *)key; seed^=*secret; uint64_t a, b; - if(_likely_(len<=16)){ - if(_likely_(len>=4)){ a=(_wyr4(p)<<32)|_wyr4(p+((len>>3)<<2)); b=(_wyr4(p+len-4)<<32)|_wyr4(p+len-4-((len>>3)<<2)); } - else if(_likely_(len>0)){ a=_wyr3(p,len); b=0;} - else a=b=0; - } - else{ + const uint8_t *p = (const uint8_t *)key; seed^=*secret; uint64_t a, b; + if (_likely_(len<=16)) { + #ifdef __BMI2__ + if (len>8) { a = _wyr8(p); b = _bzhi_u64(_wyr8(p+8), (len-8)*8); } + else { a = 0; b = _bzhi_u64(_wyr8(p ), len *8); } + #else + if (len==16) { a = _wyr8(p); b = _wyr8(p+8); } + else { + if (len>=8) { a = _wyr8(p); p+= 8; } + else a = 0; + b = _wyr8(p) & (((uint64_t)1)<<((8*len)&63))-1; + } + #endif + } else { size_t i=len; - if(_unlikely_(i>48)){ + if (_unlikely_(i>48)){ uint64_t see1=seed, see2=seed; - do{ + do { seed=_wymix(_wyr8(p)^secret[1],_wyr8(p+8)^seed); see1=_wymix(_wyr8(p+16)^secret[2],_wyr8(p+24)^see1); see2=_wymix(_wyr8(p+32)^secret[3],_wyr8(p+40)^see2); p+=48; i-=48; - }while(_likely_(i>48)); + } while (_likely_(i>48)); seed^=see1^see2; } - while(_unlikely_(i>16)){ seed=_wymix(_wyr8(p)^secret[1],_wyr8(p+8)^seed); i-=16; p+=16; } + while (_unlikely_(i>16)) { seed=_wymix(_wyr8(p)^secret[1],_wyr8(p+8)^seed); i-=16; p+=16; } a=_wyr8(p+i-16); b=_wyr8(p+i-8); } return _wymix(secret[1]^len,_wymix(a^secret[1],b^seed)); diff --git a/test/README.md b/test/README.md index c8b2af0e..3ce4d4dc 100644 --- a/test/README.md +++ b/test/README.md @@ -10,9 +10,10 @@ test/moreCfgs.sh path/to/mlochbaum/BQN // run "2+2" in a bunch of configurations ./BQN test/equal.bqn // fuzz-test 𝕨≡𝕩 ./BQN test/copy.bqn // fuzz-test creating new arrays with elements copied from another ./BQN test/bitcpy.bqn // fuzz-test bit_cpy; requires a CBQN build with -DTEST_BITCPY +./BQN test/hash.bqn // fuzz-test hashing ./BQN test/squeezeValid.bqn // fuzz-test squeezing giving a correct result; requires a CBQN build with -DEEQUAL_NEGZERO ./BQN test/squeezeExact.bqn // fuzz-test squeezing giving the exact smallest result; requires a CBQN build with -DEEQUAL_NEGZERO -./BQN test/random.bqn // various random tests +./BQN test/random.bqn // tests for various small things make -C test/ffi // test FFI functionality; expects both regular and shared library CBQN builds to already exist legacy utilities: diff --git a/test/hash.bqn b/test/hash.bqn new file mode 100644 index 00000000..5096f8a1 --- /dev/null +++ b/test/hash.bqn @@ -0,0 +1,25 @@ +# note: tests only bit/i8/i16/i32 arrays; no f64/c8/c16/c32/generic array tests +u ← ⌊100×(•UnixTime+1|100וMonoTime)@ +R ← (•MakeRand •Show u).Range + +M ← (2⋆32)⊸×⊸+´ +Test ← { 𝕊: + width ← 1‿8‿16‿32⊑˜R 4 + count ← 1+R (width=1)⊑32‿256 + data ← (width×count) R 2 + n ← 1+R count + cast ← {width=1? ⊢; ⟨1⋄width‿'i'⟩ •bit._cast} + iota ← ↕≠data + shifts ← ↕1+count-n + exp ← >(-shifts)(width/⌽)¨↕count + got ← >1↓¨∊¨<˘⍉>{ + a ← Cast data≠𝕩≠iota + shifts (M∘•Hash n↑↓)¨ ⋈<)got +} + +fails ← 0‿0 +{𝕊: fails+↩ Test@}¨↕1000 +•Out ∾⟨"Collisions: " ⋄ •Repr 0⊑fails ⋄ " (expected 0, assuming no actual hash collisions)"⟩ +•Out ∾⟨"Mismatches: " ⋄ •Repr 1⊑fails ⋄ " (expected 0)"⟩