4
4
{-# LANGUAGE RoleAnnotations #-}
5
5
{-# LANGUAGE TypeFamilies #-}
6
6
{-# LANGUAGE UnboxedTuples #-}
7
+ {-# LANGUAGE TypeSynonymInstances #-}
8
+ {-# LANGUAGE FlexibleInstances #-}
7
9
{-# LANGUAGE LambdaCase #-}
8
10
#if __GLASGOW_HASKELL__ >= 802
9
11
{-# LANGUAGE TypeInType #-}
26
28
27
29
module Data.HashMap.Internal
28
30
(
29
- HashMap (.. )
31
+ HashMapT (.. )
32
+ , HashMap
30
33
, Leaf (.. )
31
34
32
35
-- * Construction
@@ -158,6 +161,7 @@ import qualified Data.List as L
158
161
import GHC.Exts ((==#) , build , reallyUnsafePtrEquality #, inline )
159
162
import Prelude hiding (filter , foldl , foldr , lookup , map , null , pred )
160
163
import Text.Read hiding (step )
164
+ import GHC.TypeLits (Nat , natVal )
161
165
162
166
import qualified Data.HashMap.Internal.Array as A
163
167
import qualified Data.Hashable as H
@@ -196,8 +200,8 @@ import Data.Coerce (coerce)
196
200
------------------------------------------------------------------------
197
201
198
202
-- | Convenience function. Compute a hash value for the given value.
199
- hash :: H. Hashable a => a -> Hash
200
- hash = fromIntegral . H. hash
203
+ hash :: H. Hashable a => Integer -> a -> Hash
204
+ hash salt = fromIntegral . H. hashWithSalt ( fromIntegral salt)
201
205
202
206
data Leaf k v = L ! k v
203
207
deriving (Eq )
@@ -218,17 +222,27 @@ instance NF.NFData2 Leaf where
218
222
-- Invariant: The length of the 1st argument to 'Full' is
219
223
-- 2^bitsPerSubkey
220
224
225
+
226
+ #if WORD_SIZE_IN_BITS == 64
227
+ type DefaultSalt = - 2578643520546668380 -- 0xdc36d1615b7400a4
228
+ #else
229
+ type DefaultSalt = 0x087fc72c
230
+ #endif
231
+
221
232
-- | A map from keys to values. A map cannot contain duplicate keys;
222
233
-- each key can map to at most one value.
223
- data HashMap k v
234
+ data HashMapT ( salt :: Nat ) k v
224
235
= Empty
225
- | BitmapIndexed ! Bitmap ! (A. Array (HashMap k v ))
236
+ | BitmapIndexed ! Bitmap ! (A. Array (HashMapT salt k v ))
226
237
| Leaf ! Hash ! (Leaf k v )
227
- | Full ! (A. Array (HashMap k v ))
238
+ | Full ! (A. Array (HashMapT salt k v ))
228
239
| Collision ! Hash ! (A. Array (Leaf k v ))
229
240
deriving (Typeable )
230
241
231
- type role HashMap nominal representational
242
+ -- backwards compatibility
243
+ type HashMap = HashMapT DefaultSalt
244
+
245
+ type role HashMapT nominal nominal representational
232
246
233
247
instance (NFData k , NFData v ) => NFData (HashMap k v ) where
234
248
rnf Empty = ()
@@ -397,17 +411,18 @@ instance Eq k => Eq1 (HashMap k) where
397
411
--
398
412
-- In general, the lack of substitutivity can be observed with any function
399
413
-- that depends on the key ordering, such as folds and traversals.
400
- instance (Eq k , Eq v ) => Eq (HashMap k v ) where
414
+ instance (Eq k , Eq v ) => Eq (HashMapT salt k v ) where
401
415
(==) = equal1 (==)
402
416
403
417
-- We rely on there being no Empty constructors in the tree!
404
418
-- This ensures that two equal HashMaps will have the same
405
419
-- shape, modulo the order of entries in Collisions.
406
- equal1 :: Eq k
420
+ equal1 :: forall k v v' salt . Eq k
407
421
=> (v -> v' -> Bool )
408
- -> HashMap k v -> HashMap k v' -> Bool
422
+ -> HashMapT salt k v -> HashMapT salt k v' -> Bool
409
423
equal1 eq = go
410
424
where
425
+ go :: HashMapT salt k v -> HashMapT salt k v' -> Bool
411
426
go Empty Empty = True
412
427
go (BitmapIndexed bm1 ary1) (BitmapIndexed bm2 ary2)
413
428
= bm1 == bm2 && A. sameArray1 go ary1 ary2
@@ -420,7 +435,7 @@ equal1 eq = go
420
435
leafEq (L k1 v1) (L k2 v2) = k1 == k2 && eq v1 v2
421
436
422
437
equal2 :: (k -> k' -> Bool ) -> (v -> v' -> Bool )
423
- -> HashMap k v -> HashMap k' v' -> Bool
438
+ -> HashMapT salt k v -> HashMapT salt k' v' -> Bool
424
439
equal2 eqk eqv t1 t2 = go (toList' t1 [] ) (toList' t2 [] )
425
440
where
426
441
-- If the two trees are the same, then their lists of 'Leaf's and
@@ -562,7 +577,7 @@ instance (Hashable k, Hashable v) => Hashable (HashMap k v) where
562
577
arrayHashesSorted s = L. sort . L. map (hashLeafWithSalt s) . A. toList
563
578
564
579
-- Helper to get 'Leaf's and 'Collision's as a list.
565
- toList' :: HashMap k v -> [HashMap k v ] -> [HashMap k v ]
580
+ toList' :: HashMapT salt k v -> [HashMapT salt k v ] -> [HashMapT salt k v ]
566
581
toList' (BitmapIndexed _ ary) a = A. foldr toList' a ary
567
582
toList' (Full ary) a = A. foldr toList' a ary
568
583
toList' l@ (Leaf _ _) a = l : a
@@ -579,12 +594,17 @@ isLeafOrCollision _ = False
579
594
-- * Construction
580
595
581
596
-- | /O(1)/ Construct an empty map.
582
- empty :: HashMap k v
597
+ empty :: forall k v salt . HashMapT salt k v
583
598
empty = Empty
584
599
585
600
-- | /O(1)/ Construct a map with a single element.
586
601
singleton :: (Hashable k ) => k -> v -> HashMap k v
587
- singleton k v = Leaf (hash k) (L k v)
602
+ singleton = singleton'
603
+
604
+ singleton' :: forall k v salt . (Hashable k ) => k -> v -> HashMapT salt k v
605
+ singleton' k v = Leaf (hash salt k) (L k v)
606
+ where
607
+ salt = natVal (Proxy :: Proxy salt )
588
608
589
609
------------------------------------------------------------------------
590
610
-- * Basic interface
@@ -614,7 +634,7 @@ member k m = case lookup k m of
614
634
615
635
-- | /O(log n)/ Return the value to which the specified key is mapped,
616
636
-- or 'Nothing' if this map contains no mapping for the key.
617
- lookup :: (Eq k , Hashable k ) => k -> HashMap k v -> Maybe v
637
+ lookup :: (Eq k , Hashable k ) => k -> HashMapT salt k v -> Maybe v
618
638
#if __GLASGOW_HASKELL__ >= 802
619
639
-- GHC does not yet perform a worker-wrapper transformation on
620
640
-- unboxed sums automatically. That seems likely to happen at some
@@ -624,8 +644,10 @@ lookup k m = case lookup# k m of
624
644
(# | a # ) -> Just a
625
645
{-# INLINE lookup #-}
626
646
627
- lookup # :: (Eq k , Hashable k ) => k -> HashMap k v -> (# (# # ) | v # )
628
- lookup # k m = lookupCont (\ _ -> (# (# # ) | # )) (\ v _i -> (# | v # )) (hash k) k 0 m
647
+ lookup # :: (Eq k , Hashable k ) => k -> HashMapT salt k v -> (# (# # ) | v # )
648
+ lookup # k m = lookupCont (\ _ -> (# (# # ) | # )) (\ v _i -> (# | v # )) (hash salt k) k 0 m
649
+ where
650
+ salt = natVal (Proxy :: Proxy salt )
629
651
{-# INLINABLE lookup# #-}
630
652
631
653
#else
@@ -708,20 +730,20 @@ lookupRecordCollision h k m = lookupCont (\_ -> Absent) Present h k 0 m
708
730
-- keys at level @n@ of a hashmap, the offset should be @n * bitsPerSubkey@.
709
731
lookupCont ::
710
732
#if __GLASGOW_HASKELL__ >= 802
711
- forall rep (r :: TYPE rep ) k v.
733
+ forall rep (r :: TYPE rep ) k v salt .
712
734
#else
713
- forall r k v.
735
+ forall r k v salt .
714
736
#endif
715
737
Eq k
716
738
=> ((# # ) -> r) -- Absent continuation
717
739
-> (v -> Int -> r) -- Present continuation
718
740
-> Hash -- The hash of the key
719
741
-> k
720
742
-> Int -- The offset of the subkey in the hash.
721
- -> HashMap k v -> r
743
+ -> HashMapT salt k v -> r
722
744
lookupCont absent present ! h0 ! k0 ! s0 ! m0 = go h0 k0 s0 m0
723
745
where
724
- go :: Eq k => Hash -> k -> Int -> HashMap k v -> r
746
+ go :: Eq k => Hash -> k -> Int -> HashMapT salt k v -> r
725
747
go ! _ ! _ ! _ Empty = absent (# # )
726
748
go h k _ (Leaf hx (L kx x))
727
749
| h == hx && k == kx = present x (- 1 )
@@ -806,11 +828,13 @@ bitmapIndexedOrFull b ary
806
828
-- | /O(log n)/ Associate the specified value with the specified
807
829
-- key in this map. If this map previously contained a mapping for
808
830
-- the key, the old value is replaced.
809
- insert :: (Eq k , Hashable k ) => k -> v -> HashMap k v -> HashMap k v
810
- insert k v m = insert' (hash k) k v m
831
+ insert :: forall k v salt . (Eq k , Hashable k ) => k -> v -> HashMapT salt k v -> HashMapT salt k v
832
+ insert k v m = insert' (hash salt k) k v m
833
+ where
834
+ salt = natVal (Proxy :: Proxy salt )
811
835
{-# INLINABLE insert #-}
812
836
813
- insert' :: Eq k => Hash -> k -> v -> HashMap k v -> HashMap k v
837
+ insert' :: forall k v salt . Eq k => Hash -> k -> v -> HashMapT salt k v -> HashMapT salt k v
814
838
insert' h0 k0 v0 m0 = go h0 k0 v0 0 m0
815
839
where
816
840
go ! h ! k x ! _ Empty = Leaf h (L k x)
@@ -934,7 +958,7 @@ setAtPosition i k x ary = A.update ary i (L k x)
934
958
935
959
936
960
-- | In-place update version of insert
937
- unsafeInsert :: (Eq k , Hashable k ) => k -> v -> HashMap k v -> HashMap k v
961
+ unsafeInsert :: (Eq k ) => k -> v -> HashMap k v -> HashMap k v
938
962
unsafeInsert k0 v0 m0 = runST (go h0 k0 v0 0 m0)
939
963
where
940
964
h0 = hash k0
0 commit comments