lsm-tree-1.0.0.0: src-core/Database/LSMTree/Internal/Vector.hs
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# OPTIONS_HADDOCK not-home #-}
module Database.LSMTree.Internal.Vector (
mkPrimVector,
byteVectorFromPrim,
noRetainedExtraMemory,
primArrayToPrimVector,
mapStrict,
mapMStrict,
imapMStrict,
forMStrict,
zipWithStrict,
binarySearchL,
unsafeInsertWithMStrict,
unfoldrNM',
) where
import Control.Monad
import Control.Monad.Primitive (PrimMonad, PrimState)
import qualified Data.Primitive as P
import Data.Primitive.ByteArray (ByteArray, newByteArray,
runByteArray, sizeofByteArray, writeByteArray)
import Data.Primitive.Types (Prim (sizeOfType#), sizeOfType)
import Data.Proxy (Proxy (..))
import qualified Data.Vector as V
import qualified Data.Vector.Algorithms.Search as VA
import qualified Data.Vector.Mutable as VM
import qualified Data.Vector.Primitive as VP
import Data.Word (Word8)
import Database.LSMTree.Internal.Assertions
import GHC.Exts (Int (..))
import GHC.ST (runST)
mkPrimVector :: forall a. Prim a => Int -> Int -> ByteArray -> VP.Vector a
mkPrimVector off len ba =
assert (isValidSlice (off * sizeof) (len * sizeof) ba) $
VP.Vector off len ba
where
sizeof = I# (sizeOfType# (Proxy @a))
{-# INLINE mkPrimVector #-}
byteVectorFromPrim :: forall a. Prim a => a -> VP.Vector Word8
byteVectorFromPrim prim = mkPrimVector 0 (sizeOfType @a) $
runByteArray $ do
rep <- newByteArray (sizeOfType @a)
writeByteArray rep 0 prim
pure rep
{-# INLINE byteVectorFromPrim #-}
noRetainedExtraMemory :: forall a. Prim a => VP.Vector a -> Bool
noRetainedExtraMemory (VP.Vector off len ba) =
off == 0 && len * sizeof == sizeofByteArray ba
where
sizeof = I# (sizeOfType# (Proxy @a))
{-# INLINE primArrayToPrimVector #-}
primArrayToPrimVector :: Prim a => P.PrimArray a -> VP.Vector a
primArrayToPrimVector pa@(P.PrimArray ba) =
VP.Vector 0 (P.sizeofPrimArray pa) (P.ByteArray ba)
{-# INLINE mapStrict #-}
-- | /( O(n) /) Like 'V.map', but strict in the produced elements of type @b@.
mapStrict :: forall a b. (a -> b) -> V.Vector a -> V.Vector b
mapStrict f v = runST (V.mapM (\x -> pure $! f x) v)
{-# INLINE mapMStrict #-}
-- | /( O(n) /) Like 'V.mapM', but strict in the produced elements of type @b@.
mapMStrict :: Monad m => (a -> m b) -> V.Vector a -> m (V.Vector b)
mapMStrict f v = V.mapM (f >=> (pure $!)) v
{-# INLINE imapMStrict #-}
-- | /( O(n) /) Like 'V.imapM', but strict in the produced elements of type @b@.
imapMStrict :: Monad m => (Int -> a -> m b) -> V.Vector a -> m (V.Vector b)
imapMStrict f v = V.imapM (\i -> f i >=> (pure $!)) v
{-# INLINE zipWithStrict #-}
-- | /( O(min(m,n)) /) Like 'V.zipWithM', but strict in the produced elements of
-- type @c@.
zipWithStrict :: forall a b c. (a -> b -> c) -> V.Vector a -> V.Vector b -> V.Vector c
zipWithStrict f xs ys = runST (V.zipWithM (\x y -> pure $! f x y) xs ys)
-- | /( O(n) /) Like 'V.forM', but strict in the produced elements of type @b@.
{-# INLINE forMStrict #-}
forMStrict :: Monad m => V.Vector a -> (a -> m b) -> m (V.Vector b)
forMStrict xs f = V.forM xs (f >=> (pure $!))
{-|
Finds the lowest index in a given sorted vector at which the given element
could be inserted while maintaining the sortedness.
This is a variant of 'Data.Vector.Algorithms.Search.binarySearchL' for
immutable vectors.
-}
binarySearchL :: Ord a => V.Vector a -> a -> Int
binarySearchL vec val = runST $ V.unsafeThaw vec >>= flip VA.binarySearchL val
{-# INLINE binarySearchL #-}
{-# INLINE unsafeInsertWithMStrict #-}
-- | Insert (in a broad sense) an entry in a mutable vector at a given index,
-- but if a @Just@ entry already exists at that index, combine the two entries
-- using @f@.
unsafeInsertWithMStrict ::
PrimMonad m
=> VM.MVector (PrimState m) (Maybe a)
-> (a -> a -> a) -- ^ function @f@, called as @f new old@
-> Int
-> a
-> m ()
unsafeInsertWithMStrict mvec f i y = VM.unsafeModifyM mvec g i
where
g x = pure $! Just $! maybe y (`f` y) x
{-# INLINE unfoldrNM' #-}
-- | A version of 'V.unfoldrNM' that also returns the final state.
--
-- /O(n)/ Construct a vector by repeatedly applying the monadic generator
-- function to a seed. The generator function also yields 'Just' the next
-- element or 'Nothing' if there are no more elements.
--
-- The state as well as all elements of the result vector are forced to weak
-- head normal form.
unfoldrNM' :: PrimMonad m => Int -> (b -> m (Maybe a, b)) -> b -> m (V.Vector a, b)
unfoldrNM' len f = \b0 -> do
vec <- VM.unsafeNew len
go vec 0 b0
where
go !vec !n !b
| n >= len = (, b) <$!> V.unsafeFreeze vec
| otherwise =
f b >>= \case
(Nothing, !b') ->
(, b') <$!> V.unsafeFreeze (VM.slice 0 n vec)
(Just !a, !b') -> do
VM.unsafeWrite vec n a
go vec (n+1) b'