-
Notifications
You must be signed in to change notification settings - Fork 100
/
Copy pathKey.hs
69 lines (54 loc) · 1.93 KB
/
Key.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE TypeApplications #-}
module Util.Key (Key(..), keyToInt, incKey, collisionAtHash) where
import Data.Bits (bit, (.&.))
import Data.Hashable (Hashable (hashWithSalt))
import Data.Word (Word16)
import GHC.Generics (Generic)
import Test.QuickCheck (Arbitrary (..), CoArbitrary (..), Function, Gen, Large)
import qualified Test.QuickCheck as QC
-- Key type that generates more hash collisions.
data Key = K
{ hash :: !Int
-- ^ The hash of the key
, _x :: !SmallSum
-- ^ Additional data, so we can have collisions for any hash
} deriving (Eq, Ord, Read, Show, Generic, Function, CoArbitrary)
instance Hashable Key where
hashWithSalt _ (K h _) = h
data SmallSum = A | B | C | D
deriving (Eq, Ord, Read, Show, Generic, Enum, Bounded, Function, CoArbitrary)
instance Arbitrary SmallSum where
arbitrary = QC.arbitraryBoundedEnum
shrink = shrinkSmallSum
shrinkSmallSum :: SmallSum -> [SmallSum]
shrinkSmallSum A = []
shrinkSmallSum B = [A]
shrinkSmallSum C = [A, B]
shrinkSmallSum D = [A, B, C]
instance Arbitrary Key where
arbitrary = K <$> arbitraryHash <*> arbitrary
shrink = QC.genericShrink
arbitraryHash :: Gen Int
arbitraryHash = do
let gens =
[ (2, fromIntegral . QC.getLarge <$> arbitrary @(Large Word16))
, (1, QC.getSmall <$> arbitrary)
, (1, QC.getLarge <$> arbitrary)
]
i <- QC.frequency gens
moreCollisions' <- QC.elements [moreCollisions, id]
pure (moreCollisions' i)
-- | Mask out most bits to produce more collisions
moreCollisions :: Int -> Int
moreCollisions w = fromIntegral (w .&. mask)
mask :: Int
mask = sum [bit n | n <- [0, 3, 8, 14, 61]]
keyToInt :: Key -> Int
keyToInt (K h x) = h * fromEnum x
incKey :: Key -> Key
incKey (K h x) = K (h + 1) x
-- | 4 colliding keys at a given hash.
collisionAtHash :: Int -> (Key, Key, Key, Key)
collisionAtHash h = (K h A, K h B, K h C, K h D)