Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 7874a0f

Browse files
authored
Merge pull request #302 from haskell-unordered-containers/mix
Make hashInt use xxh3 inspired mixHash
2 parents 549ba54 + c5767e8 commit 7874a0f

File tree

5 files changed

+52
-30
lines changed

5 files changed

+52
-30
lines changed

‎hashable-bench/hashable-bench.cabal‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ library
6363
Data.Hashable.Generic.Instances
6464
Data.Hashable.Imports
6565
Data.Hashable.LowLevel
66+
Data.Hashable.Mix
6667

6768
c-sources: cbits/fnv.c
6869
include-dirs: include

‎hashable.cabal‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ description:
1212
The 'Hashable' 'hash' values are not guaranteed to be stable across library versions, operating systems or architectures. For stable hashing use named hashes: SHA256, CRC32 etc.
1313

1414
homepage: http://github.com/haskell-unordered-containers/hashable
15-
1615
license: BSD-3-Clause
1716
license-file: LICENSE
1817
author:
@@ -72,12 +71,13 @@ library
7271
Data.Hashable.Generic.Instances
7372
Data.Hashable.Imports
7473
Data.Hashable.LowLevel
74+
Data.Hashable.Mix
7575

7676
c-sources: cbits/fnv.c
7777
include-dirs: include
7878
hs-source-dirs: src
7979
build-depends:
80-
base >=4.12.0.0 && <4.21
80+
, base >=4.12.0.0 && <4.21
8181
, bytestring >=0.10.8.2 && <0.13
8282
, containers >=0.6.0.1 && <0.8
8383
, deepseq >=1.4.4.0 && <1.6
@@ -159,7 +159,7 @@ test-suite hashable-tests
159159
Regress
160160

161161
build-depends:
162-
base
162+
, base
163163
, bytestring
164164
, filepath
165165
, ghc-prim
@@ -187,7 +187,7 @@ test-suite hashable-tests
187187
test-suite hashable-examples
188188
type: exitcode-stdio-1.0
189189
build-depends:
190-
base
190+
, base
191191
, ghc-prim
192192
, hashable
193193

‎src/Data/Hashable/LowLevel.hs‎

Lines changed: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import System.IO.Unsafe (unsafePerformIO)
2222
#endif
2323

2424
import Data.Hashable.Imports
25+
import Data.Hashable.Mix
2526

2627
-------------------------------------------------------------------------------
2728
-- Initial seed
@@ -61,30 +62,7 @@ defaultSalt' = -2128831035 -- 2166136261 :: Int32
6162
-- | Hash 'Int'. First argument is a salt, second argument is an 'Int'.
6263
-- The result is new salt / hash value.
6364
hashInt :: Salt -> Int -> Salt
64-
hashInt s x = s `rnd` x1 `rnd` x2 `rnd` x3 `rnd` x4
65-
where
66-
{-# INLINE rnd #-}
67-
{-# INLINE x1 #-}
68-
{-# INLINE x2 #-}
69-
{-# INLINE x3 #-}
70-
{-# INLINE x4 #-}
71-
#if WORD_SIZE_IN_BITS == 64
72-
-- See https://github.com/haskell-unordered-containers/hashable/issues/270
73-
-- FNV-1 is defined to hash byte at the time.
74-
-- We used to hash whole Int at once, which provided very bad mixing.
75-
-- Current is a performance-quality compromise, we do four rounds per Int (instead of 8 for FNV-1 or 1 for previous hashable).
76-
rnd a b = (a * 1099511628211) `xor` b
77-
x1 = shiftR x 48 .&. 0xffff
78-
x2 = shiftR x 32 .&. 0xffff
79-
x3 = shiftR x 16 .&. 0xffff
80-
x4 = x .&. 0xffff
81-
#else
82-
rnd a b = (a * 16777619) `xor` b
83-
x1 = shiftR x 24 .&. 0xff
84-
x2 = shiftR x 16 .&. 0xff
85-
x3 = shiftR x 8 .&. 0xff
86-
x4 = x .&. 0xff
87-
#endif
65+
hashInt s x = fromIntegral (mixHash (fromIntegral s) (fromIntegral x))
8866

8967
-- Note: FNV-1 hash takes a byte of data at once, here we take an 'Int',
9068
-- which is 4 or 8 bytes. Whether that's bad or not, I don't know.

‎src/Data/Hashable/Mix.hs‎

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
{-# LANGUAGE CPP #-}
2+
{-# LANGUAGE MagicHash #-}
3+
{-# LANGUAGE Trustworthy #-}
4+
{-# LANGUAGE UnboxedTuples #-}
5+
module Data.Hashable.Mix (mixHash) where
6+
7+
#include "MachDeps.h"
8+
9+
import Data.Bits (unsafeShiftR, xor)
10+
import GHC.Exts (Word (..), byteSwap#, timesWord2#, xor#)
11+
12+
mulFold :: Word -> Word -> Word
13+
mulFold (W# x) (W# y) = case timesWord2# x y of
14+
(# hi, lo #) -> W# (xor# hi lo)
15+
16+
byteSwap :: Word -> Word
17+
byteSwap (W# w) = W# (byteSwap# w)
18+
19+
avalanche :: Word -> Word
20+
avalanche z0 =
21+
#if WORD_SIZE_IN_BITS == 64
22+
-- MurmurHash3Mixer
23+
let z1 = shiftXorMultiply 33 0xff51afd7ed558ccd z0
24+
z2 = shiftXorMultiply 33 0xc4ceb9fe1a85ec53 z1
25+
z3 = shiftXor 33 z2
26+
in z3
27+
#else
28+
-- MurmurHash3Mixer 32bit
29+
let z1 = shiftXorMultiply 16 0x85ebca6b z0
30+
z2 = shiftXorMultiply 13 0xc2b2ae35 z1
31+
z3 = shiftXor 16 z2
32+
in z3
33+
#endif
34+
35+
shiftXor :: Int -> Word -> Word
36+
shiftXor n w = w `xor` (w `unsafeShiftR` n)
37+
38+
shiftXorMultiply :: Int -> Word -> Word -> Word
39+
shiftXorMultiply n k w = shiftXor n w * k
40+
41+
-- | Mix hash is inspired by how xxh3 works on small (<=16byte) inputs.
42+
mixHash :: Word -> Word -> Word
43+
mixHash hi lo = avalanche (byteSwap lo + hi + mulFold hi lo)

‎tests/Regress.hs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,9 @@ regressions = [] ++
6666
, testCase "64 bit Text" $ do
6767
hash ("hello world" :: Text) @?=
6868
#if MIN_VERSION_text(2,0,0)
69-
2589482369471999198
69+
588044899381568208
7070
#else
71-
-1955893671357159554
71+
-5067133951949802236
7272
#endif
7373
#endif
7474
, F.testGroup "concatenation"

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /