hblock-0.1.0.2: Data/HBlock.hs
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DeriveDataTypeable #-}
module Data.HBlock where
import Prelude hiding (foldr, foldl)
import Data.Foldable (Foldable(..))
import Control.Monad.ST
import Data.Vector (Vector)
import qualified Data.Vector as V
import qualified Data.Vector.Mutable as VM
import Data.IntSet (IntSet)
import qualified Data.IntSet as IS
import Data.SafeCopy
import GHC.Exts (build)
import qualified Data.List as L
import Data.Data (Typeable)
-- import Data.Indexation.IxData (IxData(IxData, ixDataPos, ixData))
data HBlock a = HBlock { set :: !IntSet
, values :: (Vector a)
, size :: !Int
, numItems :: !Int
, blockLen :: !Int
, emptyVal :: !a
} deriving (Typeable)
instance Functor HBlock where
fmap f (HBlock s v len num blen e) =
if num == 0
then emptyhb
else IS.foldl transf emptyhb s
where
emptyhb = empty len blen (f e)
transf hb pos = insert (f $ V.unsafeIndex v pos) hb
instance Foldable HBlock where
foldr f z (HBlock s v _ num _ _) =
if num == 0 then z else IS.foldr' foldit z s
where
foldit pos item = f (V.unsafeIndex v pos) item
foldl f z (HBlock s v _ num _ _) =
if num == 0 then z else IS.foldl foldit z s
where
foldit item pos = f item (V.unsafeIndex v pos)
instance (SafeCopy a) => SafeCopy (HBlock a) where
version = 0
kind = base
putCopy h@(HBlock _ _ _ n bl e) = contain $ do safePut n
safePut bl
safePut e
safePut (toList h)
return ()
getCopy = contain $ do n <- safeGet
bl <- safeGet
e <- safeGet
lst <- safeGet
return $ fromList n bl e lst
-- | Creates a new HBlock
-- receives the initial size, the ammount of items that the vector gets
-- incremented when it is almost full, and an empty item to place on the growth
-- vector
empty :: Int -> Int -> a -> HBlock a
empty initSize blockSize e =
HBlock { set = IS.empty
, values = V.replicate initSize e
, size = initSize
, numItems = 0
, blockLen = blockSize
, emptyVal = e
}
{-# INLINABLE empty #-}
length :: HBlock a -> Int
length b = numItems b
{-# INLINE length#-}
-- | similar to insert but returns the index where the item was placed
insertGetIx :: a -> HBlock a -> (Int, HBlock a)
insertGetIx item b =
(pos, b { set = IS.insert pos (set b)
, values = newvec
, size = if growthNeeded then blockLen b else (size b) - 1
, numItems = numItems b + 1
})
where
pos = if IS.null (set b) then 0 else IS.findMax (set b) + 1
oldvec = values b
growthNeeded = size b == 0
growthSize = blockLen b
newvec = runST $
do v <- V.unsafeThaw oldvec
-- grow the vector by the extendAmmount
let extendIxs = [pos..(pos+growthSize)]
v' <- if growthNeeded
then do newv <- VM.unsafeGrow v growthSize
(mapM_ (\i -> VM.write newv i (emptyVal b)) extendIxs)
return newv
else return v
VM.write v' pos item
V.unsafeFreeze v'
{-# INLINABLE insertGetIx #-}
insert :: a -> HBlock a -> HBlock a
insert i b = snd $ insertGetIx i b
{-# INLINE insert #-}
adjust :: (a -> a) -> Int -> HBlock a -> HBlock a
adjust f pos b = b { values = adjusted }
where
oldvec = values b
adjusted = runST $
do v <- V.unsafeThaw oldvec
val <- VM.read v pos
VM.write v pos (f val)
V.unsafeFreeze v
{-# INLINABLE adjust #-}
update :: a -> Int -> HBlock a -> HBlock a
update new pos b = b { values = adjusted }
where
oldvec = values b
adjusted = runST $
do v <- V.unsafeThaw oldvec
VM.write v pos new
V.unsafeFreeze v
{-# INLINABLE update #-}
occupied :: Int -> HBlock a -> Bool
occupied pos b = IS.member pos (set b)
{-# INLINE occupied #-}
getPos :: HBlock a -> Int -> Maybe a
getPos hb pos = if occupied pos hb then Just $ V.unsafeIndex (values hb) pos
else Nothing
{-# INLINABLE getPos #-}
getSet :: HBlock a -> IntSet -> HBlock a
getSet hb s = hb { set = newSet
, numItems = IS.size newSet
}
where
newSet = IS.intersection s (set hb)
-- ^TODO map and copy the whole vector into the new hb ?
-- test growing the vector and then using the returned HB from this function
delete :: Int -> HBlock a -> HBlock a
delete pos b = b { set = IS.delete pos (set b)
, numItems = if occupied pos b then numItems b - 1
else numItems b
}
{-# INLINABLE delete #-}
-- | /O(n)/ Return a list of this hblock's elements. The list is
-- produced lazily.
-- toList :: HashMap k v -> [(k, v)]
-- toList t = build (\ c z -> foldrWithKey (curry c) z t)
toList :: HBlock a -> [a]
toList hb = build (\c z -> foldr c z hb)
{-# INLINE toList #-}
-- | /O(n)/ Construct a hblock with the values from a list.
fromList :: Int -> Int -> a -> [a] -> HBlock a
fromList s bs e lst = L.foldl' (flip insert) (empty s bs e) lst
{-# INLINABLE fromList #-}