{-# LANGUAGE CPP #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE UnboxedTuples #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE PatternGuards #-}
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE Trustworthy #-}
{-# LANGUAGE DeriveFunctor, DeriveFoldable, DeriveTraversable #-}
{-# OPTIONS_GHC -Wall -funbox-strict-fields -fno-warn-orphans -fno-warn-type-defaults -O2 #-}
#ifdef ST_HACK
{-# OPTIONS_GHC -fno-full-laziness #-}
#endif
module Data.Discrimination.Internal.WordMap
( WordMap
, singleton
, empty
, insert
, lookup
, member
, fromList
) where
import Control.Applicative hiding (empty)
import Control.DeepSeq
import Control.Monad.ST hiding (runST)
import Data.Bits
import Data.Discrimination.Internal.SmallArray
import Data.Foldable
import Data.Functor
import Data.Monoid
import Data.Traversable
import Data.Word
import qualified GHC.Exts as Exts
import Prelude hiding (lookup, length, foldr)
import GHC.Types
import GHC.ST
type Key = Word64
type Mask = Word16
type Offset = Int
ptrEq :: a -> a -> Bool
ptrEq x y = isTrue# (Exts.reallyUnsafePtrEquality# x y Exts.==# 1#)
{-# INLINEABLE ptrEq #-}
ptrNeq :: a -> a -> Bool
ptrNeq x y = isTrue# (Exts.reallyUnsafePtrEquality# x y Exts./=# 1#)
{-# INLINEABLE ptrNeq #-}
data WordMap v
= Full !Key !Offset !(SmallArray (WordMap v))
| Node !Key !Offset !Mask !(SmallArray (WordMap v))
| Tip !Key v
| Nil
deriving Show
node :: Key -> Offset -> Mask -> SmallArray (WordMap v) -> WordMap v
node k o 0xffff a = Full k o a
node k o m a = Node k o m a
{-# INLINE node #-}
instance NFData v => NFData (WordMap v) where
rnf (Full _ _ a) = rnf a
rnf (Node _ _ _ a) = rnf a
rnf (Tip _ v) = rnf v
rnf Nil = ()
instance Functor WordMap where
fmap f = go where
go (Full k o a) = Full k o (fmap go a)
go (Node k o m a) = Node k o m (fmap go a)
go (Tip k v) = Tip k (f v)
go Nil = Nil
{-# INLINEABLE fmap #-}
instance Foldable WordMap where
foldMap f = go where
go (Full _ _ a) = foldMap go a
go (Node _ _ _ a) = foldMap go a
go (Tip _ v) = f v
go Nil = mempty
{-# INLINEABLE foldMap #-}
instance Traversable WordMap where
traverse f = go where
go (Full k o a) = Full k o <$> traverse go a
go (Node k o m a) = Node k o m <$> traverse go a
go (Tip k v) = Tip k <$> f v
go Nil = pure Nil
{-# INLINEABLE traverse #-}
level :: Key -> Int
level w = 60 - (countLeadingZeros w .&. 0x7c)
{-# INLINE level #-}
maskBit :: Key -> Offset -> Int
maskBit k o = fromIntegral (unsafeShiftR k o .&. 0xf)
{-# INLINE maskBit #-}
mask :: Key -> Offset -> Word16
mask k o = unsafeShiftL 1 (maskBit k o)
{-# INLINE mask #-}
fork :: Int -> Key -> WordMap v -> Key -> WordMap v -> WordMap v
fork o k n ok on = Node (k .&. unsafeShiftL 0xfffffffffffffff0 o) o (mask k o .|. mask ok o) $ runST $ do
arr <- newSmallArray 2 n
writeSmallArray arr (fromEnum (k < ok)) on
unsafeFreezeSmallArray arr
insert :: Key -> v -> WordMap v -> WordMap v
insert !k v xs0 = go xs0 where
go on@(Full ok n as)
| wd > 0xf = fork (level okk) k (Tip k v) ok on
| !oz <- indexSmallArray as d
, !z <- go oz
, ptrNeq z oz = Full ok n (update16 d z as)
| otherwise = on
where
okk = xor ok k
wd = unsafeShiftR okk n
d = fromIntegral wd
go on@(Node ok n m as)
| wd > 0xf = fork (level okk) k (Tip k v) ok on
| m .&. b == 0 = node ok n (m .|. b) (insertSmallArray odm (Tip k v) as)
| !oz <- indexSmallArray as odm
, !z <- go oz
, ptrNeq z oz = Node ok n m (updateSmallArray odm z as)
| otherwise = on
where
okk = xor ok k
wd = unsafeShiftR okk n
d = fromIntegral wd
b = unsafeShiftL 1 d
odm = popCount $ m .&. (b - 1)
go on@(Tip ok ov)
| k /= ok = fork (level (xor ok k)) k (Tip k v) ok on
| ptrEq v ov = on
| otherwise = Tip k v
go Nil = Tip k v
{-# INLINEABLE insert #-}
lookup :: Key -> WordMap v -> Maybe v
lookup !k (Full ok o a)
| z <- unsafeShiftR (xor k ok) o, z <= 0xf = lookup k $ indexSmallArray a (fromIntegral z)
| otherwise = Nothing
lookup k (Node ok o m a)
| z <= 0xf && m .&. b /= 0 = lookup k (indexSmallArray a (popCount (m .&. (b - 1))))
| otherwise = Nothing
where
z = unsafeShiftR (xor k ok) o
b = unsafeShiftL 1 (fromIntegral z)
lookup k (Tip ok ov)
| k == ok = Just ov
| otherwise = Nothing
lookup _ Nil = Nothing
{-# INLINEABLE lookup #-}
member :: Key -> WordMap v -> Bool
member !k (Full ok o a)
| z <- unsafeShiftR (xor k ok) o = z <= 0xf && member k (indexSmallArray a (fromIntegral z))
member k (Node ok o m a)
| z <- unsafeShiftR (xor k ok) o
= z <= 0xf && let b = unsafeShiftL 1 (fromIntegral z) in
m .&. b /= 0 && member k (indexSmallArray a (popCount (m .&. (b - 1))))
member k (Tip ok _) = k == ok
member _ Nil = False
{-# INLINEABLE member #-}
updateSmallArray :: Int -> a -> SmallArray a -> SmallArray a
updateSmallArray !k a i = runST $ do
let n = length i
o <- newSmallArray n undefined
copySmallArray o 0 i 0 n
writeSmallArray o k a
unsafeFreezeSmallArray o
{-# INLINEABLE updateSmallArray #-}
update16 :: Int -> a -> SmallArray a -> SmallArray a
update16 !k a i = runST $ do
o <- clone16 i
writeSmallArray o k a
unsafeFreezeSmallArray o
{-# INLINEABLE update16 #-}
insertSmallArray :: Int -> a -> SmallArray a -> SmallArray a
insertSmallArray !k a i = runST $ do
let n = length i
o <- newSmallArray (n + 1) a
copySmallArray o 0 i 0 k
copySmallArray o (k+1) i k (n-k)
unsafeFreezeSmallArray o
{-# INLINEABLE insertSmallArray #-}
clone16 :: SmallArray a -> ST s (SmallMutableArray s a)
clone16 i = do
o <- newSmallArray 16 undefined
indexSmallArrayM i 0 >>= writeSmallArray o 0
indexSmallArrayM i 1 >>= writeSmallArray o 1
indexSmallArrayM i 2 >>= writeSmallArray o 2
indexSmallArrayM i 3 >>= writeSmallArray o 3
indexSmallArrayM i 4 >>= writeSmallArray o 4
indexSmallArrayM i 5 >>= writeSmallArray o 5
indexSmallArrayM i 6 >>= writeSmallArray o 6
indexSmallArrayM i 7 >>= writeSmallArray o 7
indexSmallArrayM i 8 >>= writeSmallArray o 8
indexSmallArrayM i 9 >>= writeSmallArray o 9
indexSmallArrayM i 10 >>= writeSmallArray o 10
indexSmallArrayM i 11 >>= writeSmallArray o 11
indexSmallArrayM i 12 >>= writeSmallArray o 12
indexSmallArrayM i 13 >>= writeSmallArray o 13
indexSmallArrayM i 14 >>= writeSmallArray o 14
indexSmallArrayM i 15 >>= writeSmallArray o 15
return o
{-# INLINE clone16 #-}
singleton :: Key -> v -> WordMap v
singleton !k v = Tip k v
{-# INLINE singleton #-}
fromList :: [(Word64,v)] -> WordMap v
fromList xs = foldl' (\r (k,v) -> insert k v r) Nil xs
{-# INLINE fromList #-}
empty :: WordMap a
empty = Nil
{-# INLINE empty #-}