diff --git a/Data/HashMap/Array.hs b/Data/HashMap/Array.hs index 0149da4d..426979b3 100644 --- a/Data/HashMap/Array.hs +++ b/Data/HashMap/Array.hs @@ -5,8 +5,8 @@ -- -- Note that no bounds checking are performed. module Data.HashMap.Array - ( Array - , MArray + ( SmallArray + , SmallMutableArray -- * Creation , new @@ -16,13 +16,13 @@ module Data.HashMap.Array , pair -- * Basic interface - , length - , lengthM - , read - , write - , index - , indexM - , index# + , sizeofSmallArray + , sizeofSmallMutableArray + , readSmallArray + , writeSmallArray + , indexSmallArray + , indexSmallArrayM + , indexSmallArray## , update , updateWith' , unsafeUpdateM @@ -32,25 +32,28 @@ module Data.HashMap.Array , sameArray1 , trim - , unsafeFreeze - , unsafeThaw + , unsafeFreezeSmallArray + , unsafeThawSmallArray , unsafeSameArray , run , run2 - , copy - , copyM + , copySmallArray + , copySmallMutableArray -- * Folds , foldl' , foldr - , thaw + , thawSmallArray , map - , map' + , mapSmallArray' , traverse , traverse' , toList , fromList + + -- * Forcing + , rnfArray ) where #if !MIN_VERSION_base(4,8,0) @@ -61,6 +64,8 @@ import Control.DeepSeq import GHC.Exts(Int(..), Int#, reallyUnsafePtrEquality#, tagToEnum#, unsafeCoerce#, State#) import GHC.ST (ST(..)) import Control.Monad.ST (stToIO) +import Data.Primitive.SmallArray +import qualified Data.Traversable #if __GLASGOW_HASKELL__ >= 709 import Prelude hiding (filter, foldr, length, map, read, traverse) @@ -68,19 +73,6 @@ import Prelude hiding (filter, foldr, length, map, read, traverse) import Prelude hiding (filter, foldr, length, map, read) #endif -#if __GLASGOW_HASKELL__ >= 710 -import GHC.Exts (SmallArray#, newSmallArray#, readSmallArray#, writeSmallArray#, - indexSmallArray#, unsafeFreezeSmallArray#, unsafeThawSmallArray#, - SmallMutableArray#, sizeofSmallArray#, copySmallArray#, thawSmallArray#, - sizeofSmallMutableArray#, copySmallMutableArray#, cloneSmallMutableArray#) - -#else -import GHC.Exts (Array#, newArray#, readArray#, writeArray#, - indexArray#, unsafeFreezeArray#, unsafeThawArray#, - MutableArray#, sizeofArray#, copyArray#, thawArray#, - sizeofMutableArray#, copyMutableArray#, cloneMutableArray#) -#endif - #if defined(ASSERTS) import qualified Prelude #endif @@ -88,72 +80,6 @@ import qualified Prelude import Data.HashMap.Unsafe (runST) import Control.Monad ((>=>)) - -#if __GLASGOW_HASKELL__ >= 710 -type Array# a = SmallArray# a -type MutableArray# a = SmallMutableArray# a - -newArray# :: Int# -> a -> State# d -> (# State# d, SmallMutableArray# d a #) -newArray# = newSmallArray# - -unsafeFreezeArray# :: SmallMutableArray# d a - -> State# d -> (# State# d, SmallArray# a #) -unsafeFreezeArray# = unsafeFreezeSmallArray# - -readArray# :: SmallMutableArray# d a - -> Int# -> State# d -> (# State# d, a #) -readArray# = readSmallArray# - -writeArray# :: SmallMutableArray# d a - -> Int# -> a -> State# d -> State# d -writeArray# = writeSmallArray# - -indexArray# :: SmallArray# a -> Int# -> (# a #) -indexArray# = indexSmallArray# - -unsafeThawArray# :: SmallArray# a - -> State# d -> (# State# d, SmallMutableArray# d a #) -unsafeThawArray# = unsafeThawSmallArray# - -sizeofArray# :: SmallArray# a -> Int# -sizeofArray# = sizeofSmallArray# - -copyArray# :: SmallArray# a - -> Int# - -> SmallMutableArray# d a - -> Int# - -> Int# - -> State# d - -> State# d -copyArray# = copySmallArray# - -cloneMutableArray# :: SmallMutableArray# s a - -> Int# - -> Int# - -> State# s - -> (# State# s, SmallMutableArray# s a #) -cloneMutableArray# = cloneSmallMutableArray# - -thawArray# :: SmallArray# a - -> Int# - -> Int# - -> State# d - -> (# State# d, SmallMutableArray# d a #) -thawArray# = thawSmallArray# - -sizeofMutableArray# :: SmallMutableArray# s a -> Int# -sizeofMutableArray# = sizeofSmallMutableArray# - -copyMutableArray# :: SmallMutableArray# d a - -> Int# - -> SmallMutableArray# d a - -> Int# - -> Int# - -> State# d - -> State# d -copyMutableArray# = copySmallMutableArray# -#endif - ------------------------------------------------------------------------ #if defined(ASSERTS) @@ -174,257 +100,151 @@ if not ((_lhs_) _op_ (_rhs_)) then error ("Data.HashMap.Array." ++ (_func_) ++ " # define CHECK_EQ(_func_,_lhs_,_rhs_) #endif -data Array a = Array { - unArray :: !(Array# a) - } - -instance Show a => Show (Array a) where - show = show . toList - -- Determines whether two arrays have the same memory address. -- This is more reliable than testing pointer equality on the -- Array wrappers, but it's still slightly bogus. -unsafeSameArray :: Array a -> Array b -> Bool -unsafeSameArray (Array xs) (Array ys) = +unsafeSameArray :: SmallArray a -> SmallArray b -> Bool +unsafeSameArray (SmallArray xs) (SmallArray ys) = tagToEnum# (unsafeCoerce# reallyUnsafePtrEquality# xs ys) -sameArray1 :: (a -> b -> Bool) -> Array a -> Array b -> Bool +sameArray1 :: (a -> b -> Bool) -> SmallArray a -> SmallArray b -> Bool sameArray1 eq !xs0 !ys0 | lenxs /= lenys = False | otherwise = go 0 xs0 ys0 where go !k !xs !ys | k == lenxs = True - | (# x #) <- index# xs k - , (# y #) <- index# ys k + | (# x #) <- indexSmallArray## xs k + , (# y #) <- indexSmallArray## ys k = eq x y && go (k + 1) xs ys - !lenxs = length xs0 - !lenys = length ys0 - -length :: Array a -> Int -length ary = I# (sizeofArray# (unArray ary)) -{-# INLINE length #-} - --- | Smart constructor -array :: Array# a -> Int -> Array a -array ary _n = Array ary -{-# INLINE array #-} - -data MArray s a = MArray { - unMArray :: !(MutableArray# s a) - } - -lengthM :: MArray s a -> Int -lengthM mary = I# (sizeofMutableArray# (unMArray mary)) -{-# INLINE lengthM #-} - --- | Smart constructor -marray :: MutableArray# s a -> Int -> MArray s a -marray mary _n = MArray mary -{-# INLINE marray #-} + !lenxs = sizeofSmallArray xs0 + !lenys = sizeofSmallArray ys0 ------------------------------------------------------------------------ -instance NFData a => NFData (Array a) where - rnf = rnfArray - -rnfArray :: NFData a => Array a -> () +rnfArray :: NFData a => SmallArray a -> () rnfArray ary0 = go ary0 n0 0 where - n0 = length ary0 + n0 = sizeofSmallArray ary0 go !ary !n !i | i >= n = () - | (# x #) <- index# ary i + | (# x #) <- indexSmallArray## ary i = rnf x `seq` go ary n (i+1) --- We use index# just in case GHC can't see that the +-- We use indexSmallArray## just in case GHC can't see that the -- relevant rnf is strict, or in case it actually isn't. {-# INLINE rnfArray #-} -- | Create a new mutable array of specified size, in the specified -- state thread, with each element containing the specified initial -- value. -new :: Int -> a -> ST s (MArray s a) -new n@(I# n#) b = - CHECK_GT("new",n,(0 :: Int)) - ST $ \s -> - case newArray# n# b s of - (# s', ary #) -> (# s', marray ary n #) -{-# INLINE new #-} - -new_ :: Int -> ST s (MArray s a) +new :: Int -> a -> ST s (SmallMutableArray s a) +new = newSmallArray + +new_ :: Int -> ST s (SmallMutableArray s a) new_ n = new n undefinedElem -singleton :: a -> Array a +singleton :: a -> SmallArray a singleton x = runST (singletonM x) {-# INLINE singleton #-} -singletonM :: a -> ST s (Array a) -singletonM x = new 1 x >>= unsafeFreeze +singletonM :: a -> ST s (SmallArray a) +singletonM x = new 1 x >>= unsafeFreezeSmallArray {-# INLINE singletonM #-} -pair :: a -> a -> Array a +pair :: a -> a -> SmallArray a pair x y = run $ do ary <- new 2 x - write ary 1 y + writeSmallArray ary 1 y return ary {-# INLINE pair #-} -read :: MArray s a -> Int -> ST s a -read ary _i@(I# i#) = ST $ \ s -> - CHECK_BOUNDS("read", lengthM ary, _i) - readArray# (unMArray ary) i# s -{-# INLINE read #-} - -write :: MArray s a -> Int -> a -> ST s () -write ary _i@(I# i#) b = ST $ \ s -> - CHECK_BOUNDS("write", lengthM ary, _i) - case writeArray# (unMArray ary) i# b s of - s' -> (# s' , () #) -{-# INLINE write #-} - -index :: Array a -> Int -> a -index ary _i@(I# i#) = - CHECK_BOUNDS("index", length ary, _i) - case indexArray# (unArray ary) i# of (# b #) -> b -{-# INLINE index #-} - -index# :: Array a -> Int -> (# a #) -index# ary _i@(I# i#) = - CHECK_BOUNDS("index#", length ary, _i) - indexArray# (unArray ary) i# -{-# INLINE index# #-} - -indexM :: Array a -> Int -> ST s a -indexM ary _i@(I# i#) = - CHECK_BOUNDS("indexM", length ary, _i) - case indexArray# (unArray ary) i# of (# b #) -> return b -{-# INLINE indexM #-} - -unsafeFreeze :: MArray s a -> ST s (Array a) -unsafeFreeze mary - = ST $ \s -> case unsafeFreezeArray# (unMArray mary) s of - (# s', ary #) -> (# s', array ary (lengthM mary) #) -{-# INLINE unsafeFreeze #-} - -unsafeThaw :: Array a -> ST s (MArray s a) -unsafeThaw ary - = ST $ \s -> case unsafeThawArray# (unArray ary) s of - (# s', mary #) -> (# s', marray mary (length ary) #) -{-# INLINE unsafeThaw #-} - -run :: (forall s . ST s (MArray s e)) -> Array e -run act = runST $ act >>= unsafeFreeze +run :: (forall s . ST s (SmallMutableArray s e)) -> SmallArray e +run act = runST $ act >>= unsafeFreezeSmallArray {-# INLINE run #-} -run2 :: (forall s. ST s (MArray s e, a)) -> (Array e, a) +run2 :: (forall s. ST s (SmallMutableArray s e, a)) -> (SmallArray e, a) run2 k = runST (do (marr,b) <- k - arr <- unsafeFreeze marr + arr <- unsafeFreezeSmallArray marr return (arr,b)) --- | Unsafely copy the elements of an array. Array bounds are not checked. -copy :: Array e -> Int -> MArray s e -> Int -> Int -> ST s () -copy !src !_sidx@(I# sidx#) !dst !_didx@(I# didx#) _n@(I# n#) = - CHECK_LE("copy", _sidx + _n, length src) - CHECK_LE("copy", _didx + _n, lengthM dst) - ST $ \ s# -> - case copyArray# (unArray src) sidx# (unMArray dst) didx# n# s# of - s2 -> (# s2, () #) - --- | Unsafely copy the elements of an array. Array bounds are not checked. -copyM :: MArray s e -> Int -> MArray s e -> Int -> Int -> ST s () -copyM !src !_sidx@(I# sidx#) !dst !_didx@(I# didx#) _n@(I# n#) = - CHECK_BOUNDS("copyM: src", lengthM src, _sidx + _n - 1) - CHECK_BOUNDS("copyM: dst", lengthM dst, _didx + _n - 1) - ST $ \ s# -> - case copyMutableArray# (unMArray src) sidx# (unMArray dst) didx# n# s# of - s2 -> (# s2, () #) - -cloneM :: MArray s a -> Int -> Int -> ST s (MArray s a) -cloneM _mary@(MArray mary#) _off@(I# off#) _len@(I# len#) = - CHECK_BOUNDS("cloneM_off", lengthM _mary, _off - 1) - CHECK_BOUNDS("cloneM_end", lengthM _mary, _off + _len - 1) - ST $ \ s -> - case cloneMutableArray# mary# off# len# s of - (# s', mary'# #) -> (# s', MArray mary'# #) - -- | Create a new array of the @n@ first elements of @mary@. -trim :: MArray s a -> Int -> ST s (Array a) -trim mary n = cloneM mary 0 n >>= unsafeFreeze +trim :: SmallMutableArray s a -> Int -> ST s (SmallArray a) +trim mary n = cloneSmallMutableArray mary 0 n >>= unsafeFreezeSmallArray {-# INLINE trim #-} -- | /O(n)/ Insert an element at the given position in this array, -- increasing its size by one. -insert :: Array e -> Int -> e -> Array e +insert :: SmallArray e -> Int -> e -> SmallArray e insert ary idx b = runST (insertM ary idx b) {-# INLINE insert #-} -- | /O(n)/ Insert an element at the given position in this array, -- increasing its size by one. -insertM :: Array e -> Int -> e -> ST s (Array e) +insertM :: SmallArray e -> Int -> e -> ST s (SmallArray e) insertM ary idx b = CHECK_BOUNDS("insertM", count + 1, idx) do mary <- new_ (count+1) - copy ary 0 mary 0 idx - write mary idx b - copy ary idx mary (idx+1) (count-idx) - unsafeFreeze mary - where !count = length ary + copySmallArray mary 0 ary 0 idx + writeSmallArray mary idx b + copySmallArray mary (idx+1) ary idx (count-idx) + unsafeFreezeSmallArray mary + where !count = sizeofSmallArray ary {-# INLINE insertM #-} -- | /O(n)/ Update the element at the given position in this array. -update :: Array e -> Int -> e -> Array e +update :: SmallArray e -> Int -> e -> SmallArray e update ary idx b = runST (updateM ary idx b) {-# INLINE update #-} -- | /O(n)/ Update the element at the given position in this array. -updateM :: Array e -> Int -> e -> ST s (Array e) +updateM :: SmallArray e -> Int -> e -> ST s (SmallArray e) updateM ary idx b = CHECK_BOUNDS("updateM", count, idx) - do mary <- thaw ary 0 count - write mary idx b - unsafeFreeze mary - where !count = length ary + do mary <- thawSmallArray ary 0 count + writeSmallArray mary idx b + unsafeFreezeSmallArray mary + where !count = sizeofSmallArray ary {-# INLINE updateM #-} -- | /O(n)/ Update the element at the given positio in this array, by -- applying a function to it. Evaluates the element to WHNF before -- inserting it into the array. -updateWith' :: Array e -> Int -> (e -> e) -> Array e +updateWith' :: SmallArray e -> Int -> (e -> e) -> SmallArray e updateWith' ary idx f - | (# x #) <- index# ary idx + | (# x #) <- indexSmallArray## ary idx = update ary idx $! f x {-# INLINE updateWith' #-} -- | /O(1)/ Update the element at the given position in this array, -- without copying. -unsafeUpdateM :: Array e -> Int -> e -> ST s () +unsafeUpdateM :: SmallArray e -> Int -> e -> ST s () unsafeUpdateM ary idx b = - CHECK_BOUNDS("unsafeUpdateM", length ary, idx) - do mary <- unsafeThaw ary - write mary idx b - _ <- unsafeFreeze mary + CHECK_BOUNDS("unsafeUpdateM", sizeofSmallArray ary, idx) + do mary <- unsafeThawSmallArray ary + writeSmallArray mary idx b + _ <- unsafeFreezeSmallArray mary return () {-# INLINE unsafeUpdateM #-} -foldl' :: (b -> a -> b) -> b -> Array a -> b -foldl' f = \ z0 ary0 -> go ary0 (length ary0) 0 z0 +foldl' :: (b -> a -> b) -> b -> SmallArray a -> b +foldl' f = \ z0 ary0 -> go ary0 (sizeofSmallArray ary0) 0 z0 where go ary n i !z | i >= n = z | otherwise - = case index# ary i of + = case indexSmallArray## ary i of (# x #) -> go ary n (i+1) (f z x) {-# INLINE foldl' #-} -foldr :: (a -> b -> b) -> b -> Array a -> b -foldr f = \ z0 ary0 -> go ary0 (length ary0) 0 z0 +foldr :: (a -> b -> b) -> b -> SmallArray a -> b +foldr f = \ z0 ary0 -> go ary0 (sizeofSmallArray ary0) 0 z0 where go ary n i z | i >= n = z | otherwise - = case index# ary i of + = case indexSmallArray## ary i of (# x #) -> f x (go ary n (i+1) z) {-# INLINE foldr #-} @@ -432,34 +252,27 @@ undefinedElem :: a undefinedElem = error "Data.HashMap.Array: Undefined element" {-# NOINLINE undefinedElem #-} -thaw :: Array e -> Int -> Int -> ST s (MArray s e) -thaw !ary !_o@(I# o#) !n@(I# n#) = - CHECK_LE("thaw", _o + n, length ary) - ST $ \ s -> case thawArray# (unArray ary) o# n# s of - (# s2, mary# #) -> (# s2, marray mary# n #) -{-# INLINE thaw #-} - -- | /O(n)/ Delete an element at the given position in this array, -- decreasing its size by one. -delete :: Array e -> Int -> Array e +delete :: SmallArray e -> Int -> SmallArray e delete ary idx = runST (deleteM ary idx) {-# INLINE delete #-} -- | /O(n)/ Delete an element at the given position in this array, -- decreasing its size by one. -deleteM :: Array e -> Int -> ST s (Array e) +deleteM :: SmallArray e -> Int -> ST s (SmallArray e) deleteM ary idx = do CHECK_BOUNDS("deleteM", count, idx) do mary <- new_ (count-1) - copy ary 0 mary 0 idx - copy ary (idx+1) mary idx (count-(idx+1)) - unsafeFreeze mary - where !count = length ary + copySmallArray mary 0 ary 0 idx + copySmallArray mary idx ary (idx+1) (count-(idx+1)) + unsafeFreezeSmallArray mary + where !count = sizeofSmallArray ary {-# INLINE deleteM #-} -map :: (a -> b) -> Array a -> Array b +map :: (a -> b) -> SmallArray a -> SmallArray b map f = \ ary -> - let !n = length ary + let !n = sizeofSmallArray ary in run $ do mary <- new_ n go ary mary 0 n @@ -467,28 +280,12 @@ map f = \ ary -> go ary mary i n | i >= n = return mary | otherwise = do - x <- indexM ary i - write mary i $ f x + x <- indexSmallArrayM ary i + writeSmallArray mary i $ f x go ary mary (i+1) n {-# INLINE map #-} --- | Strict version of 'map'. -map' :: (a -> b) -> Array a -> Array b -map' f = \ ary -> - let !n = length ary - in run $ do - mary <- new_ n - go ary mary 0 n - where - go ary mary i n - | i >= n = return mary - | otherwise = do - x <- indexM ary i - write mary i $! f x - go ary mary (i+1) n -{-# INLINE map' #-} - -fromList :: Int -> [a] -> Array a +fromList :: Int -> [a] -> SmallArray a fromList n xs0 = CHECK_EQ("fromList", n, Prelude.length xs0) run $ do @@ -496,88 +293,16 @@ fromList n xs0 = go xs0 mary 0 where go [] !mary !_ = return mary - go (x:xs) mary i = do write mary i x + go (x:xs) mary i = do writeSmallArray mary i x go xs mary (i+1) -toList :: Array a -> [a] +toList :: SmallArray a -> [a] toList = foldr (:) [] -newtype STA a = STA {_runSTA :: forall s. MutableArray# s a -> ST s (Array a)} - -runSTA :: Int -> STA a -> Array a -runSTA !n (STA m) = runST $ new_ n >>= \ (MArray ar) -> m ar - -traverse :: Applicative f => (a -> f b) -> Array a -> f (Array b) -traverse f = \ !ary -> - let - !len = length ary - go !i - | i == len = pure $ STA $ \mary -> unsafeFreeze (MArray mary) - | (# x #) <- index# ary i - = liftA2 (\b (STA m) -> STA $ \mary -> - write (MArray mary) i b >> m mary) - (f x) (go (i + 1)) - in runSTA len <$> go 0 -{-# INLINE [1] traverse #-} - --- TODO: Would it be better to just use a lazy traversal --- and then force the elements of the result? My guess is --- yes. -traverse' :: Applicative f => (a -> f b) -> Array a -> f (Array b) -traverse' f = \ !ary -> - let - !len = length ary - go !i - | i == len = pure $ STA $ \mary -> unsafeFreeze (MArray mary) - | (# x #) <- index# ary i - = liftA2 (\ !b (STA m) -> STA $ \mary -> - write (MArray mary) i b >> m mary) - (f x) (go (i + 1)) - in runSTA len <$> go 0 -{-# INLINE [1] traverse' #-} - --- Traversing in ST, we don't need to get fancy; we --- can just do it directly. -traverseST :: (a -> ST s b) -> Array a -> ST s (Array b) -traverseST f = \ ary0 -> - let - !len = length ary0 - go k !mary - | k == len = return mary - | otherwise = do - x <- indexM ary0 k - y <- f x - write mary k y - go (k + 1) mary - in new_ len >>= (go 0 >=> unsafeFreeze) -{-# INLINE traverseST #-} - -traverseIO :: (a -> IO b) -> Array a -> IO (Array b) -traverseIO f = \ ary0 -> - let - !len = length ary0 - go k !mary - | k == len = return mary - | otherwise = do - x <- stToIO $ indexM ary0 k - y <- f x - stToIO $ write mary k y - go (k + 1) mary - in stToIO (new_ len) >>= (go 0 >=> stToIO . unsafeFreeze) -{-# INLINE traverseIO #-} - - --- Why don't we have similar RULES for traverse'? The efficient --- way to traverse strictly in IO or ST is to force results as --- they come in, which leads to different semantics. In particular, --- we need to ensure that --- --- traverse' (\x -> print x *> pure undefined) xs --- --- will actually print all the values and then return undefined. --- We could add a strict mapMWithIndex, operating in an arbitrary --- Monad, that supported such rules, but we don't have that right now. -{-# RULES -"traverse/ST" forall f. traverse f = traverseST f -"traverse/IO" forall f. traverse f = traverseIO f - #-} +traverse :: Applicative f => (a -> f b) -> SmallArray a -> f (SmallArray b) +traverse = Data.Traversable.traverse + +-- definitely fix this +traverse' :: Applicative f => (a -> f b) -> SmallArray a -> f (SmallArray b) +traverse' = Data.Traversable.traverse + diff --git a/Data/HashMap/Base.hs b/Data/HashMap/Base.hs index 35d1fceb..f31be978 100644 --- a/Data/HashMap/Base.hs +++ b/Data/HashMap/Base.hs @@ -178,20 +178,20 @@ instance (NFData k, NFData v) => NFData (Leaf k v) where -- each key can map to at most one value. data HashMap k v = Empty - | BitmapIndexed !Bitmap !(A.Array (HashMap k v)) + | BitmapIndexed !Bitmap !(A.SmallArray (HashMap k v)) | Leaf !Hash !(Leaf k v) - | Full !(A.Array (HashMap k v)) - | Collision !Hash !(A.Array (Leaf k v)) + | Full !(A.SmallArray (HashMap k v)) + | Collision !Hash !(A.SmallArray (Leaf k v)) deriving (Typeable) type role HashMap nominal representational instance (NFData k, NFData v) => NFData (HashMap k v) where rnf Empty = () - rnf (BitmapIndexed _ ary) = rnf ary + rnf (BitmapIndexed _ ary) = A.rnfArray ary rnf (Leaf _ l) = rnf l - rnf (Full ary) = rnf ary - rnf (Collision _ ary) = rnf ary + rnf (Full ary) = A.rnfArray ary + rnf (Collision _ ary) = A.rnfArray ary instance Functor (HashMap k) where fmap = map @@ -313,7 +313,7 @@ equal2 eqk eqv t1 t2 = go (toList' t1 []) (toList' t2 []) = go tl1 tl2 go (Collision k1 ary1 : tl1) (Collision k2 ary2 : tl2) | k1 == k2 && - A.length ary1 == A.length ary2 && + A.sizeofSmallArray ary1 == A.sizeofSmallArray ary2 && isPermutationBy leafEq (A.toList ary1) (A.toList ary2) = go tl1 tl2 go [] [] = True @@ -347,7 +347,7 @@ cmp cmpk cmpv t1 t2 = go (toList' t1 []) (toList' t2 []) go tl1 tl2 go (Collision k1 ary1 : tl1) (Collision k2 ary2 : tl2) = compare k1 k2 `mappend` - compare (A.length ary1) (A.length ary2) `mappend` + compare (A.sizeofSmallArray ary1) (A.sizeofSmallArray ary2) `mappend` unorderedCompare leafCompare (A.toList ary1) (A.toList ary2) `mappend` go tl1 tl2 go (Leaf _ _ : _) (Collision _ _ : _) = LT @@ -367,7 +367,7 @@ equalKeys1 eq t1 t2 = go (toList' t1 []) (toList' t2 []) | k1 == k2 && leafEq l1 l2 = go tl1 tl2 go (Collision k1 ary1 : tl1) (Collision k2 ary2 : tl2) - | k1 == k2 && A.length ary1 == A.length ary2 && + | k1 == k2 && A.sizeofSmallArray ary1 == A.sizeofSmallArray ary2 && isPermutationBy leafEq (A.toList ary1) (A.toList ary2) = go tl1 tl2 go [] [] = True @@ -408,11 +408,11 @@ instance H.Hashable2 HashMap where -- hashLeafWithSalt :: Int -> Leaf k v -> Int hashLeafWithSalt s (L k v) = (s `hk` k) `hv` v - -- hashCollisionWithSalt :: Int -> A.Array (Leaf k v) -> Int + -- hashCollisionWithSalt :: Int -> A.SmallArray (Leaf k v) -> Int hashCollisionWithSalt s = L.foldl' H.hashWithSalt s . arrayHashesSorted s - -- arrayHashesSorted :: Int -> A.Array (Leaf k v) -> [Int] + -- arrayHashesSorted :: Int -> A.SmallArray (Leaf k v) -> [Int] arrayHashesSorted s = L.sort . L.map (hashLeafWithSalt s) . A.toList instance (Hashable k) => H.Hashable1 (HashMap k) where @@ -435,11 +435,11 @@ instance (Hashable k, Hashable v) => Hashable (HashMap k v) where hashLeafWithSalt :: Int -> Leaf k v -> Int hashLeafWithSalt s (L k v) = s `H.hashWithSalt` k `H.hashWithSalt` v - hashCollisionWithSalt :: Int -> A.Array (Leaf k v) -> Int + hashCollisionWithSalt :: Int -> A.SmallArray (Leaf k v) -> Int hashCollisionWithSalt s = L.foldl' H.hashWithSalt s . arrayHashesSorted s - arrayHashesSorted :: Int -> A.Array (Leaf k v) -> [Int] + arrayHashesSorted :: Int -> A.SmallArray (Leaf k v) -> [Int] arrayHashesSorted s = L.sort . L.map (hashLeafWithSalt s) . A.toList -- Helper to get 'Leaf's and 'Collision's as a list. @@ -483,7 +483,7 @@ size t = go t 0 go (Leaf _ _) n = n + 1 go (BitmapIndexed _ ary) n = A.foldl' (flip go) n ary go (Full ary) n = A.foldl' (flip go) n ary - go (Collision _ ary) n = n + A.length ary + go (Collision _ ary) n = n + A.sizeofSmallArray ary -- | /O(log n)/ Return 'True' if the specified key is present in the -- map, 'False' otherwise. @@ -604,10 +604,10 @@ lookupCont absent present !h0 !k0 !m0 = go h0 k0 0 m0 go h k s (BitmapIndexed b v) | b .&. m == 0 = absent (# #) | otherwise = - go h k (s+bitsPerSubkey) (A.index v (sparseIndex b m)) + go h k (s+bitsPerSubkey) (A.indexSmallArray v (sparseIndex b m)) where m = mask h s go h k s (Full v) = - go h k (s+bitsPerSubkey) (A.index v (index h s)) + go h k (s+bitsPerSubkey) (A.indexSmallArray v (index h s)) go h k _ (Collision hx v) | h == hx = lookupInArrayCont absent present k v | otherwise = absent (# #) @@ -637,13 +637,13 @@ infixl 9 ! collision :: Hash -> Leaf k v -> Leaf k v -> HashMap k v collision h !e1 !e2 = let v = A.run $ do mary <- A.new 2 e1 - A.write mary 1 e2 + A.writeSmallArray mary 1 e2 return mary in Collision h v {-# INLINE collision #-} -- | Create a 'BitmapIndexed' or 'Full' node. -bitmapIndexedOrFull :: Bitmap -> A.Array (HashMap k v) -> HashMap k v +bitmapIndexedOrFull :: Bitmap -> A.SmallArray (HashMap k v) -> HashMap k v bitmapIndexedOrFull b ary | b == fullNodeMask = Full ary | otherwise = BitmapIndexed b ary @@ -672,7 +672,7 @@ insert' h0 k0 v0 m0 = go h0 k0 v0 0 m0 let !ary' = A.insert ary i $! Leaf h (L k x) in bitmapIndexedOrFull (b .|. m) ary' | otherwise = - let !st = A.index ary i + let !st = A.indexSmallArray ary i !st' = go h k x (s+bitsPerSubkey) st in if st' `ptrEq` st then t @@ -680,7 +680,7 @@ insert' h0 k0 v0 m0 = go h0 k0 v0 0 m0 where m = mask h s i = sparseIndex b m go h k x s t@(Full ary) = - let !st = A.index ary i + let !st = A.indexSmallArray ary i !st' = go h k x (s+bitsPerSubkey) st in if st' `ptrEq` st then t @@ -710,13 +710,13 @@ insertNewKey !h0 !k0 x0 !m0 = go h0 k0 x0 0 m0 let !ary' = A.insert ary i $! Leaf h (L k x) in bitmapIndexedOrFull (b .|. m) ary' | otherwise = - let !st = A.index ary i + let !st = A.indexSmallArray ary i !st' = go h k x (s+bitsPerSubkey) st in BitmapIndexed b (A.update ary i st') where m = mask h s i = sparseIndex b m go h k x s (Full ary) = - let !st = A.index ary i + let !st = A.indexSmallArray ary i !st' = go h k x (s+bitsPerSubkey) st in Full (update16 ary i st') where i = index h s @@ -725,12 +725,12 @@ insertNewKey !h0 !k0 x0 !m0 = go h0 k0 x0 0 m0 | otherwise = go h k x s $ BitmapIndexed (mask hy s) (A.singleton t) where - snocNewLeaf :: Leaf k v -> A.Array (Leaf k v) -> A.Array (Leaf k v) + snocNewLeaf :: Leaf k v -> A.SmallArray (Leaf k v) -> A.SmallArray (Leaf k v) snocNewLeaf leaf ary = A.run $ do - let n = A.length ary + let n = A.sizeofSmallArray ary mary <- A.new_ (n + 1) - A.copy ary 0 mary 0 n - A.write mary n leaf + A.copySmallArray mary 0 ary 0 n + A.writeSmallArray mary n leaf return mary {-# NOINLINE insertNewKey #-} @@ -754,13 +754,13 @@ insertKeyExists !collPos0 !h0 !k0 x0 !m0 = go collPos0 h0 k0 x0 0 m0 let !ary' = A.insert ary i $ Leaf h (L k x) in bitmapIndexedOrFull (b .|. m) ary' | otherwise = - let !st = A.index ary i + let !st = A.indexSmallArray ary i !st' = go collPos h k x (s+bitsPerSubkey) st in BitmapIndexed b (A.update ary i st') where m = mask h s i = sparseIndex b m go collPos h k x s (Full ary) = - let !st = A.index ary i + let !st = A.indexSmallArray ary i !st' = go collPos h k x (s+bitsPerSubkey) st in Full (update16 ary i st') where i = index h s @@ -774,7 +774,7 @@ insertKeyExists !collPos0 !h0 !k0 x0 !m0 = go collPos0 h0 k0 x0 0 m0 -- Replace the ith Leaf with Leaf k v. -- -- This does not check that @i@ is within bounds of the array. -setAtPosition :: Int -> k -> v -> A.Array (Leaf k v) -> A.Array (Leaf k v) +setAtPosition :: Int -> k -> v -> A.SmallArray (Leaf k v) -> A.SmallArray (Leaf k v) setAtPosition i k x ary = A.update ary i (L k x) {-# INLINE setAtPosition #-} @@ -797,14 +797,14 @@ unsafeInsert k0 v0 m0 = runST (go h0 k0 v0 0 m0) ary' <- A.insertM ary i $! Leaf h (L k x) return $! bitmapIndexedOrFull (b .|. m) ary' | otherwise = do - st <- A.indexM ary i + st <- A.indexSmallArrayM ary i st' <- go h k x (s+bitsPerSubkey) st A.unsafeUpdateM ary i st' return t where m = mask h s i = sparseIndex b m go h k x s t@(Full ary) = do - st <- A.indexM ary i + st <- A.indexSmallArrayM ary i st' <- go h k x (s+bitsPerSubkey) st A.unsafeUpdateM ary i st' return t @@ -825,8 +825,8 @@ two = go return $! BitmapIndexed bp1 ary | otherwise = do mary <- A.new 2 $ Leaf h1 (L k1 v1) - A.write mary idx2 $ Leaf h2 (L k2 v2) - ary <- A.unsafeFreeze mary + A.writeSmallArray mary idx2 $ Leaf h2 (L k2 v2) + ary <- A.unsafeFreezeSmallArray mary return $! BitmapIndexed (bp1 .|. bp2) ary where bp1 = mask h1 s @@ -872,7 +872,7 @@ insertModifying x f k0 m0 = go h0 k0 0 m0 let ary' = A.insert ary i $! Leaf h (L k x) in bitmapIndexedOrFull (b .|. m) ary' | otherwise = - let !st = A.index ary i + let !st = A.indexSmallArray ary i !st' = go h k (s+bitsPerSubkey) st ary' = A.update ary i $! st' in if ptrEq st st' @@ -881,7 +881,7 @@ insertModifying x f k0 m0 = go h0 k0 0 m0 where m = mask h s i = sparseIndex b m go h k s t@(Full ary) = - let !st = A.index ary i + let !st = A.indexSmallArray ary i !st' = go h k (s+bitsPerSubkey) st ary' = update16 ary i $! st' in if ptrEq st st' @@ -898,18 +898,18 @@ insertModifying x f k0 m0 = go h0 k0 0 m0 {-# INLINABLE insertModifying #-} -- Like insertModifying for arrays; used to implement insertModifying -insertModifyingArr :: Eq k => v -> (v -> (# v #)) -> k -> A.Array (Leaf k v) - -> A.Array (Leaf k v) -insertModifyingArr x f k0 ary0 = go k0 ary0 0 (A.length ary0) +insertModifyingArr :: Eq k => v -> (v -> (# v #)) -> k -> A.SmallArray (Leaf k v) + -> A.SmallArray (Leaf k v) +insertModifyingArr x f k0 ary0 = go k0 ary0 0 (A.sizeofSmallArray ary0) where go !k !ary !i !n | i >= n = A.run $ do -- Not found, append to the end. mary <- A.new_ (n + 1) - A.copy ary 0 mary 0 n - A.write mary n (L k x) + A.copySmallArray mary 0 ary 0 n + A.writeSmallArray mary n (L k x) return mary - | otherwise = case A.index ary i of + | otherwise = case A.indexSmallArray ary i of (L kx y) | k == kx -> case f y of (# y' #) -> if ptrEq y y' then ary @@ -936,14 +936,14 @@ unsafeInsertWith f k0 v0 m0 = runST (go h0 k0 v0 0 m0) ary' <- A.insertM ary i $! Leaf h (L k x) return $! bitmapIndexedOrFull (b .|. m) ary' | otherwise = do - st <- A.indexM ary i + st <- A.indexSmallArrayM ary i st' <- go h k x (s+bitsPerSubkey) st A.unsafeUpdateM ary i st' return t where m = mask h s i = sparseIndex b m go h k x s t@(Full ary) = do - st <- A.indexM ary i + st <- A.indexSmallArrayM ary i st' <- go h k x (s+bitsPerSubkey) st A.unsafeUpdateM ary i st' return t @@ -969,26 +969,26 @@ delete' h0 k0 m0 = go h0 k0 0 m0 go h k s t@(BitmapIndexed b ary) | b .&. m == 0 = t | otherwise = - let !st = A.index ary i + let !st = A.indexSmallArray ary i !st' = go h k (s+bitsPerSubkey) st in if st' `ptrEq` st then t else case st' of - Empty | A.length ary == 1 -> Empty - | A.length ary == 2 -> - case (i, A.index ary 0, A.index ary 1) of + Empty | A.sizeofSmallArray ary == 1 -> Empty + | A.sizeofSmallArray ary == 2 -> + case (i, A.indexSmallArray ary 0, A.indexSmallArray ary 1) of (0, _, l) | isLeafOrCollision l -> l (1, l, _) | isLeafOrCollision l -> l _ -> bIndexed | otherwise -> bIndexed where bIndexed = BitmapIndexed (b .&. complement m) (A.delete ary i) - l | isLeafOrCollision l && A.length ary == 1 -> l + l | isLeafOrCollision l && A.sizeofSmallArray ary == 1 -> l _ -> BitmapIndexed b (A.update ary i st') where m = mask h s i = sparseIndex b m go h k s t@(Full ary) = - let !st = A.index ary i + let !st = A.indexSmallArray ary i !st' = go h k (s+bitsPerSubkey) st in if st' `ptrEq` st then t @@ -1002,10 +1002,10 @@ delete' h0 k0 m0 = go h0 k0 0 m0 go h k _ t@(Collision hy v) | h == hy = case indexOf k v of Just i - | A.length v == 2 -> + | A.sizeofSmallArray v == 2 -> if i == 0 - then Leaf h (A.index v 1) - else Leaf h (A.index v 0) + then Leaf h (A.indexSmallArray v 1) + else Leaf h (A.indexSmallArray v 0) | otherwise -> Collision h (A.delete v i) Nothing -> t | otherwise = t @@ -1025,24 +1025,24 @@ deleteKeyExists !collPos0 !h0 !k0 !m0 = go collPos0 h0 k0 0 m0 go :: Int -> Hash -> k -> Int -> HashMap k v -> HashMap k v go !_collPos !_h !_k !_s (Leaf _ _) = Empty go collPos h k s (BitmapIndexed b ary) = - let !st = A.index ary i + let !st = A.indexSmallArray ary i !st' = go collPos h k (s+bitsPerSubkey) st in case st' of - Empty | A.length ary == 1 -> Empty - | A.length ary == 2 -> - case (i, A.index ary 0, A.index ary 1) of + Empty | A.sizeofSmallArray ary == 1 -> Empty + | A.sizeofSmallArray ary == 2 -> + case (i, A.indexSmallArray ary 0, A.indexSmallArray ary 1) of (0, _, l) | isLeafOrCollision l -> l (1, l, _) | isLeafOrCollision l -> l _ -> bIndexed | otherwise -> bIndexed where bIndexed = BitmapIndexed (b .&. complement m) (A.delete ary i) - l | isLeafOrCollision l && A.length ary == 1 -> l + l | isLeafOrCollision l && A.sizeofSmallArray ary == 1 -> l _ -> BitmapIndexed b (A.update ary i st') where m = mask h s i = sparseIndex b m go collPos h k s (Full ary) = - let !st = A.index ary i + let !st = A.indexSmallArray ary i !st' = go collPos h k (s+bitsPerSubkey) st in case st' of Empty -> @@ -1052,10 +1052,10 @@ deleteKeyExists !collPos0 !h0 !k0 !m0 = go collPos0 h0 k0 0 m0 _ -> Full (A.update ary i st') where i = index h s go collPos h _ _ (Collision _hy v) - | A.length v == 2 + | A.sizeofSmallArray v == 2 = if collPos == 0 - then Leaf h (A.index v 1) - else Leaf h (A.index v 0) + then Leaf h (A.indexSmallArray v 1) + else Leaf h (A.indexSmallArray v 0) | otherwise = Collision h (A.delete v collPos) go !_ !_ !_ !_ Empty = Empty -- error "Internal error: deleteKeyExists empty" {-# NOINLINE deleteKeyExists #-} @@ -1086,7 +1086,7 @@ adjust# f k0 m0 = go h0 k0 0 m0 | otherwise = t go h k s t@(BitmapIndexed b ary) | b .&. m == 0 = t - | otherwise = let !st = A.index ary i + | otherwise = let !st = A.indexSmallArray ary i !st' = go h k (s+bitsPerSubkey) st ary' = A.update ary i $! st' in if ptrEq st st' @@ -1096,7 +1096,7 @@ adjust# f k0 m0 = go h0 k0 0 m0 i = sparseIndex b m go h k s t@(Full ary) = let i = index h s - !st = A.index ary i + !st = A.indexSmallArray ary i !st' = go h k (s+bitsPerSubkey) st ary' = update16 ary i $! st' in if ptrEq st st' @@ -1376,8 +1376,8 @@ unionWithKey f = go 0 {-# INLINE unionWithKey #-} -- | Strict in the result of @f@. -unionArrayBy :: (a -> a -> a) -> Bitmap -> Bitmap -> A.Array a -> A.Array a - -> A.Array a +unionArrayBy :: (a -> a -> a) -> Bitmap -> Bitmap -> A.SmallArray a -> A.SmallArray a + -> A.SmallArray a unionArrayBy f b1 b2 ary1 ary2 = A.run $ do let b' = b1 .|. b2 mary <- A.new_ (popCount b') @@ -1388,15 +1388,15 @@ unionArrayBy f b1 b2 ary1 ary2 = A.run $ do | m > b' = return () | b' .&. m == 0 = go i i1 i2 (m `unsafeShiftL` 1) | ba .&. m /= 0 = do - x1 <- A.indexM ary1 i1 - x2 <- A.indexM ary2 i2 - A.write mary i $! f x1 x2 + x1 <- A.indexSmallArrayM ary1 i1 + x2 <- A.indexSmallArrayM ary2 i2 + A.writeSmallArray mary i $! f x1 x2 go (i+1) (i1+1) (i2+1) (m `unsafeShiftL` 1) | b1 .&. m /= 0 = do - A.write mary i =<< A.indexM ary1 i1 + A.writeSmallArray mary i =<< A.indexSmallArrayM ary1 i1 go (i+1) (i1+1) (i2 ) (m `unsafeShiftL` 1) | otherwise = do - A.write mary i =<< A.indexM ary2 i2 + A.writeSmallArray mary i =<< A.indexSmallArrayM ary2 i2 go (i+1) (i1 ) (i2+1) (m `unsafeShiftL` 1) go 0 0 0 (b' .&. negate b') -- XXX: b' must be non-zero return mary @@ -1426,7 +1426,7 @@ mapWithKey f = go -- Why map strictly over collision arrays? Because there's no -- point suspending the O(1) work this does for each leaf. go (Collision h ary) = Collision h $ - A.map' (\ (L k v) -> L k (f k v)) ary + A.mapSmallArray' (\ (L k v) -> L k (f k v)) ary {-# INLINE mapWithKey #-} -- | /O(n)/ Transform this map by applying a function to every value. @@ -1614,19 +1614,19 @@ filterMapAux onLeaf onColl = go go (Collision h ary) = filterC ary h filterA ary0 b0 = - let !n = A.length ary0 + let !n = A.sizeofSmallArray ary0 in runST $ do mary <- A.new_ n step ary0 mary b0 0 0 1 n where - step :: A.Array (HashMap k v1) -> A.MArray s (HashMap k v2) + step :: A.SmallArray (HashMap k v1) -> A.SmallMutableArray s (HashMap k v2) -> Bitmap -> Int -> Int -> Bitmap -> Int -> ST s (HashMap k v2) step !ary !mary !b i !j !bi n | i >= n = case j of 0 -> return Empty 1 -> do - ch <- A.read mary 0 + ch <- A.readSmallArray mary 0 case ch of t | isLeafOrCollision t -> return t _ -> BitmapIndexed b <$> A.trim mary 1 @@ -1636,32 +1636,32 @@ filterMapAux onLeaf onColl = go then Full ary2 else BitmapIndexed b ary2 | bi .&. b == 0 = step ary mary b i j (bi `unsafeShiftL` 1) n - | otherwise = case go (A.index ary i) of + | otherwise = case go (A.indexSmallArray ary i) of Empty -> step ary mary (b .&. complement bi) (i+1) j (bi `unsafeShiftL` 1) n - t -> do A.write mary j t + t -> do A.writeSmallArray mary j t step ary mary b (i+1) (j+1) (bi `unsafeShiftL` 1) n filterC ary0 h = - let !n = A.length ary0 + let !n = A.sizeofSmallArray ary0 in runST $ do mary <- A.new_ n step ary0 mary 0 0 n where - step :: A.Array (Leaf k v1) -> A.MArray s (Leaf k v2) + step :: A.SmallArray (Leaf k v1) -> A.SmallMutableArray s (Leaf k v2) -> Int -> Int -> Int -> ST s (HashMap k v2) step !ary !mary i !j n | i >= n = case j of 0 -> return Empty - 1 -> do l <- A.read mary 0 + 1 -> do l <- A.readSmallArray mary 0 return $! Leaf h l - _ | i == j -> do ary2 <- A.unsafeFreeze mary + _ | i == j -> do ary2 <- A.unsafeFreezeSmallArray mary return $! Collision h ary2 | otherwise -> do ary2 <- A.trim mary j return $! Collision h ary2 - | Just el <- onColl $! A.index ary i - = A.write mary j el >> step ary mary (i+1) (j+1) n + | Just el <- onColl $! A.indexSmallArray ary i + = A.writeSmallArray mary j el >> step ary mary (i+1) (j+1) n | otherwise = step ary mary (i+1) j n {-# INLINE filterMapAux #-} @@ -1721,13 +1721,13 @@ lookupInArrayCont :: #else forall r k v. #endif - Eq k => ((# #) -> r) -> (v -> Int -> r) -> k -> A.Array (Leaf k v) -> r -lookupInArrayCont absent present k0 ary0 = go k0 ary0 0 (A.length ary0) + Eq k => ((# #) -> r) -> (v -> Int -> r) -> k -> A.SmallArray (Leaf k v) -> r +lookupInArrayCont absent present k0 ary0 = go k0 ary0 0 (A.sizeofSmallArray ary0) where - go :: Eq k => k -> A.Array (Leaf k v) -> Int -> Int -> r + go :: Eq k => k -> A.SmallArray (Leaf k v) -> Int -> Int -> r go !k !ary !i !n | i >= n = absent (# #) - | otherwise = case A.index ary i of + | otherwise = case A.indexSmallArray ary i of (L kx v) | k == kx -> present v i | otherwise -> go k ary (i+1) n @@ -1735,23 +1735,23 @@ lookupInArrayCont absent present k0 ary0 = go k0 ary0 0 (A.length ary0) -- | /O(n)/ Lookup the value associated with the given key in this -- array. Returns 'Nothing' if the key wasn't found. -indexOf :: Eq k => k -> A.Array (Leaf k v) -> Maybe Int -indexOf k0 ary0 = go k0 ary0 0 (A.length ary0) +indexOf :: Eq k => k -> A.SmallArray (Leaf k v) -> Maybe Int +indexOf k0 ary0 = go k0 ary0 0 (A.sizeofSmallArray ary0) where go !k !ary !i !n | i >= n = Nothing - | otherwise = case A.index ary i of + | otherwise = case A.indexSmallArray ary i of (L kx _) | k == kx -> Just i | otherwise -> go k ary (i+1) n {-# INLINABLE indexOf #-} -updateWith# :: Eq k => (v -> (# v #)) -> k -> A.Array (Leaf k v) -> A.Array (Leaf k v) -updateWith# f k0 ary0 = go k0 ary0 0 (A.length ary0) +updateWith# :: Eq k => (v -> (# v #)) -> k -> A.SmallArray (Leaf k v) -> A.SmallArray (Leaf k v) +updateWith# f k0 ary0 = go k0 ary0 0 (A.sizeofSmallArray ary0) where go !k !ary !i !n | i >= n = ary - | otherwise = case A.index ary i of + | otherwise = case A.indexSmallArray ary i of (L kx y) | k == kx -> case f y of (# y' #) | ptrEq y y' -> ary @@ -1759,57 +1759,57 @@ updateWith# f k0 ary0 = go k0 ary0 0 (A.length ary0) | otherwise -> go k ary (i+1) n {-# INLINABLE updateWith# #-} -updateOrSnocWith :: Eq k => (v -> v -> v) -> k -> v -> A.Array (Leaf k v) - -> A.Array (Leaf k v) +updateOrSnocWith :: Eq k => (v -> v -> v) -> k -> v -> A.SmallArray (Leaf k v) + -> A.SmallArray (Leaf k v) updateOrSnocWith f = updateOrSnocWithKey (const f) {-# INLINABLE updateOrSnocWith #-} -updateOrSnocWithKey :: Eq k => (k -> v -> v -> v) -> k -> v -> A.Array (Leaf k v) - -> A.Array (Leaf k v) -updateOrSnocWithKey f k0 v0 ary0 = go k0 v0 ary0 0 (A.length ary0) +updateOrSnocWithKey :: Eq k => (k -> v -> v -> v) -> k -> v -> A.SmallArray (Leaf k v) + -> A.SmallArray (Leaf k v) +updateOrSnocWithKey f k0 v0 ary0 = go k0 v0 ary0 0 (A.sizeofSmallArray ary0) where go !k v !ary !i !n | i >= n = A.run $ do -- Not found, append to the end. mary <- A.new_ (n + 1) - A.copy ary 0 mary 0 n - A.write mary n (L k v) + A.copySmallArray mary 0 ary 0 n + A.writeSmallArray mary n (L k v) return mary - | otherwise = case A.index ary i of + | otherwise = case A.indexSmallArray ary i of (L kx y) | k == kx -> A.update ary i (L k (f k v y)) | otherwise -> go k v ary (i+1) n {-# INLINABLE updateOrSnocWithKey #-} -updateOrConcatWith :: Eq k => (v -> v -> v) -> A.Array (Leaf k v) -> A.Array (Leaf k v) -> A.Array (Leaf k v) +updateOrConcatWith :: Eq k => (v -> v -> v) -> A.SmallArray (Leaf k v) -> A.SmallArray (Leaf k v) -> A.SmallArray (Leaf k v) updateOrConcatWith f = updateOrConcatWithKey (const f) {-# INLINABLE updateOrConcatWith #-} -updateOrConcatWithKey :: Eq k => (k -> v -> v -> v) -> A.Array (Leaf k v) -> A.Array (Leaf k v) -> A.Array (Leaf k v) +updateOrConcatWithKey :: Eq k => (k -> v -> v -> v) -> A.SmallArray (Leaf k v) -> A.SmallArray (Leaf k v) -> A.SmallArray (Leaf k v) updateOrConcatWithKey f ary1 ary2 = A.run $ do -- TODO: instead of mapping and then folding, should we traverse? -- We'll have to be careful to avoid allocating pairs or similar. -- first: look up the position of each element of ary2 in ary1 - let indices = A.map' (\(L k _) -> indexOf k ary1) ary2 + let indices = A.mapSmallArray' (\(L k _) -> indexOf k ary1) ary2 -- that tells us how large the overlap is: -- count number of Nothing constructors let nOnly2 = A.foldl' (\n -> maybe (n+1) (const n)) 0 indices - let n1 = A.length ary1 - let n2 = A.length ary2 + let n1 = A.sizeofSmallArray ary1 + let n2 = A.sizeofSmallArray ary2 -- copy over all elements from ary1 mary <- A.new_ (n1 + nOnly2) - A.copy ary1 0 mary 0 n1 + A.copySmallArray mary 0 ary1 0 n1 -- append or update all elements from ary2 let go !iEnd !i2 | i2 >= n2 = return () - | otherwise = case A.index indices i2 of + | otherwise = case A.indexSmallArray indices i2 of Just i1 -> do -- key occurs in both arrays, store combination in position i1 - L k v1 <- A.indexM ary1 i1 - L _ v2 <- A.indexM ary2 i2 - A.write mary i1 (L k (f k v1 v2)) + L k v1 <- A.indexSmallArrayM ary1 i1 + L _ v2 <- A.indexSmallArrayM ary2 i2 + A.writeSmallArray mary i1 (L k (f k v1 v2)) go iEnd (i2+1) Nothing -> do -- key is only in ary2, append to end - A.write mary iEnd =<< A.indexM ary2 i2 + A.writeSmallArray mary iEnd =<< A.indexSmallArrayM ary2 i2 go (iEnd+1) (i2+1) go n1 0 return mary @@ -1819,30 +1819,30 @@ updateOrConcatWithKey f ary1 ary2 = A.run $ do -- Manually unrolled loops -- | /O(n)/ Update the element at the given position in this array. -update16 :: A.Array e -> Int -> e -> A.Array e +update16 :: A.SmallArray e -> Int -> e -> A.SmallArray e update16 ary idx b = runST (update16M ary idx b) {-# INLINE update16 #-} -- | /O(n)/ Update the element at the given position in this array. -update16M :: A.Array e -> Int -> e -> ST s (A.Array e) +update16M :: A.SmallArray e -> Int -> e -> ST s (A.SmallArray e) update16M ary idx b = do mary <- clone16 ary - A.write mary idx b - A.unsafeFreeze mary + A.writeSmallArray mary idx b + A.unsafeFreezeSmallArray mary {-# INLINE update16M #-} -- | /O(n)/ Update the element at the given position in this array, by applying a function to it. -update16With' :: A.Array e -> Int -> (e -> e) -> A.Array e +update16With' :: A.SmallArray e -> Int -> (e -> e) -> A.SmallArray e update16With' ary idx f - | (# x #) <- A.index# ary idx + | (# x #) <- A.indexSmallArray## ary idx = update16 ary idx $! f x {-# INLINE update16With' #-} -- | Unsafely clone an array of 16 elements. The length of the input -- array is not checked. -clone16 :: A.Array e -> ST s (A.MArray s e) +clone16 :: A.SmallArray e -> ST s (A.SmallMutableArray s e) clone16 ary = - A.thaw ary 0 16 + A.thawSmallArray ary 0 16 ------------------------------------------------------------------------ -- Bit twiddling diff --git a/Data/HashMap/Strict/Base.hs b/Data/HashMap/Strict/Base.hs index 700b613e..398e2940 100644 --- a/Data/HashMap/Strict/Base.hs +++ b/Data/HashMap/Strict/Base.hs @@ -162,14 +162,14 @@ insertWith f k0 v0 m0 = go h0 k0 v0 0 m0 let ary' = A.insert ary i $! leaf h k x in bitmapIndexedOrFull (b .|. m) ary' | otherwise = - let st = A.index ary i + let st = A.indexSmallArray ary i st' = go h k x (s+bitsPerSubkey) st ary' = A.update ary i $! st' in BitmapIndexed b ary' where m = mask h s i = sparseIndex b m go h k x s (Full ary) = - let st = A.index ary i + let st = A.indexSmallArray ary i st' = go h k x (s+bitsPerSubkey) st ary' = update16 ary i $! st' in Full ary' @@ -198,14 +198,14 @@ unsafeInsertWith f k0 v0 m0 = runST (go h0 k0 v0 0 m0) ary' <- A.insertM ary i $! leaf h k x return $! bitmapIndexedOrFull (b .|. m) ary' | otherwise = do - st <- A.indexM ary i + st <- A.indexSmallArrayM ary i st' <- go h k x (s+bitsPerSubkey) st A.unsafeUpdateM ary i st' return t where m = mask h s i = sparseIndex b m go h k x s t@(Full ary) = do - st <- A.indexM ary i + st <- A.indexSmallArrayM ary i st' <- go h k x (s+bitsPerSubkey) st A.unsafeUpdateM ary i st' return t @@ -227,7 +227,7 @@ adjust f k0 m0 = go h0 k0 0 m0 | otherwise = t go h k s t@(BitmapIndexed b ary) | b .&. m == 0 = t - | otherwise = let st = A.index ary i + | otherwise = let st = A.indexSmallArray ary i st' = go h k (s+bitsPerSubkey) st ary' = A.update ary i $! st' in BitmapIndexed b ary' @@ -235,7 +235,7 @@ adjust f k0 m0 = go h0 k0 0 m0 i = sparseIndex b m go h k s (Full ary) = let i = index h s - st = A.index ary i + st = A.indexSmallArray ary i st' = go h k (s+bitsPerSubkey) st ary' = update16 ary i $! st' in Full ary' @@ -491,10 +491,10 @@ mapWithKey f = go where go Empty = Empty go (Leaf h (L k v)) = leaf h k (f k v) - go (BitmapIndexed b ary) = BitmapIndexed b $ A.map' go ary - go (Full ary) = Full $ A.map' go ary + go (BitmapIndexed b ary) = BitmapIndexed b $ A.mapSmallArray' go ary + go (Full ary) = Full $ A.mapSmallArray' go ary go (Collision h ary) = - Collision h $ A.map' (\ (L k v) -> let !v' = f k v in L k v') ary + Collision h $ A.mapSmallArray' (\ (L k v) -> let !v' = f k v in L k v') ary {-# INLINE mapWithKey #-} -- | /O(n)/ Transform this map by applying a function to every value. @@ -618,12 +618,12 @@ fromListWith f = L.foldl' (\ m (k, v) -> unsafeInsertWith f k v m) empty ------------------------------------------------------------------------ -- Array operations -updateWith :: Eq k => (v -> v) -> k -> A.Array (Leaf k v) -> A.Array (Leaf k v) -updateWith f k0 ary0 = go k0 ary0 0 (A.length ary0) +updateWith :: Eq k => (v -> v) -> k -> A.SmallArray (Leaf k v) -> A.SmallArray (Leaf k v) +updateWith f k0 ary0 = go k0 ary0 0 (A.sizeofSmallArray ary0) where go !k !ary !i !n | i >= n = ary - | otherwise = case A.index ary i of + | otherwise = case A.indexSmallArray ary i of (L kx y) | k == kx -> let !v' = f y in A.update ary i (L k v') | otherwise -> go k ary (i+1) n {-# INLINABLE updateWith #-} @@ -633,8 +633,8 @@ updateWith f k0 ary0 = go k0 ary0 0 (A.length ary0) -- the given function to the new and old value (in that order). The -- value is always evaluated to WHNF before being inserted into the -- array. -updateOrSnocWith :: Eq k => (v -> v -> v) -> k -> v -> A.Array (Leaf k v) - -> A.Array (Leaf k v) +updateOrSnocWith :: Eq k => (v -> v -> v) -> k -> v -> A.SmallArray (Leaf k v) + -> A.SmallArray (Leaf k v) updateOrSnocWith f = updateOrSnocWithKey (const f) {-# INLINABLE updateOrSnocWith #-} @@ -643,19 +643,19 @@ updateOrSnocWith f = updateOrSnocWithKey (const f) -- the given function to the new and old value (in that order). The -- value is always evaluated to WHNF before being inserted into the -- array. -updateOrSnocWithKey :: Eq k => (k -> v -> v -> v) -> k -> v -> A.Array (Leaf k v) - -> A.Array (Leaf k v) -updateOrSnocWithKey f k0 v0 ary0 = go k0 v0 ary0 0 (A.length ary0) +updateOrSnocWithKey :: Eq k => (k -> v -> v -> v) -> k -> v -> A.SmallArray (Leaf k v) + -> A.SmallArray (Leaf k v) +updateOrSnocWithKey f k0 v0 ary0 = go k0 v0 ary0 0 (A.sizeofSmallArray ary0) where go !k v !ary !i !n | i >= n = A.run $ do -- Not found, append to the end. mary <- A.new_ (n + 1) - A.copy ary 0 mary 0 n + A.copySmallArray mary 0 ary 0 n let !l = v `seq` (L k v) - A.write mary n l + A.writeSmallArray mary n l return mary - | otherwise = case A.index ary i of + | otherwise = case A.indexSmallArray ary i of (L kx y) | k == kx -> let !v' = f k v y in A.update ary i (L k v') | otherwise -> go k v ary (i+1) n {-# INLINABLE updateOrSnocWithKey #-} diff --git a/benchmarks/unordered-containers-benchmarks.cabal b/benchmarks/unordered-containers-benchmarks.cabal index 46e3e9aa..acf14cbb 100644 --- a/benchmarks/unordered-containers-benchmarks.cabal +++ b/benchmarks/unordered-containers-benchmarks.cabal @@ -5,21 +5,10 @@ build-type: Simple cabal-version: >=1.2 executable unordered-containers-benchmarks - ghc-options: -Wall -O2 - cc-options: -msse4.1 - c-sources: - ../cbits/popc.c - hs-source-dirs: .. . + ghc-options: -Wall -O2 -rtsopts + hs-source-dirs: . main-is: Benchmarks.hs other-modules: - Data.HashMap.Array - Data.HashMap.Base - Data.HashMap.Lazy - Data.HashMap.PopCount - Data.HashMap.Strict - Data.HashMap.Unsafe - Data.HashMap.UnsafeShift - Data.HashSet Util.ByteString Util.Int Util.String @@ -34,4 +23,5 @@ executable unordered-containers-benchmarks ghc-prim, hashable, mtl, - random + random, + unordered-containers diff --git a/cabal.local.project b/cabal.local.project new file mode 100644 index 00000000..2fc37f7e --- /dev/null +++ b/cabal.local.project @@ -0,0 +1,2 @@ +packages: . ../primitive ./benchmarks + diff --git a/unordered-containers.cabal b/unordered-containers.cabal index 95f84581..7a2b48dd 100644 --- a/unordered-containers.cabal +++ b/unordered-containers.cabal @@ -43,7 +43,8 @@ library build-depends: base >= 4.7 && < 5, deepseq >= 1.1, - hashable >= 1.0.1.1 && < 1.3 + hashable >= 1.0.1.1 && < 1.3, + primitive >= 0.6 default-language: Haskell2010 @@ -174,45 +175,6 @@ test-suite strictness-properties ghc-options: -Wall cpp-options: -DASSERTS -benchmark benchmarks - -- We cannot depend on the unordered-containers library directly as - -- that creates a dependency cycle. - hs-source-dirs: . benchmarks - - main-is: Benchmarks.hs - type: exitcode-stdio-1.0 - - other-modules: - Data.HashMap.Array - Data.HashMap.Base - Data.HashMap.Lazy - Data.HashMap.Strict - Data.HashMap.Strict.Base - Data.HashMap.Unsafe - Data.HashMap.UnsafeShift - Data.HashSet - Data.HashSet.Base - Util.ByteString - Util.Int - Util.String - - build-depends: - base >= 4.8.0, - bytestring, - containers, - criterion >= 1.0 && < 1.3, - deepseq >= 1.1, - deepseq-generics, - hashable >= 1.0.1.1, - hashmap, - mtl, - random - - default-language: Haskell2010 - ghc-options: -Wall -O2 -rtsopts -fwarn-tabs -ferror-spans - if flag(debug) - cpp-options: -DASSERTS - source-repository head type: git location: https://github.com/tibbe/unordered-containers.git