tinyXml-0.1.0.0: src/Data/VectorBuilder/Storable.hs
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE PartialTypeSignatures #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE CPP #-}
module Data.VectorBuilder.Storable
(VectorBuilder, new, indexFromEnd, insert, push, pop, updateStackElt, getCount, getStackCount, finalize, unsafeToMVector, peek
) where
import Control.Exception (assert)
import Control.Monad
import Control.Monad.ST.Unsafe
import Control.Monad.Primitive
import Data.Int
import Data.Primitive.MutVar
import qualified Data.Vector.Storable as S
import qualified Data.Vector.Storable.Mutable as SM
import qualified Data.Vector.Generic.Mutable as M
--import qualified Data.Vector.Storable.Mutable as M
import qualified Data.Vector.Unboxed.Mutable as U
import Foreign (Storable(sizeOf), Ptr, plusPtr)
import Foreign.ForeignPtr
import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
import Config
import Text.Printf
default (Int, Double)
data VectorBuilder s a =
VectorBuilder
{
-- We use an unboxed vector instead of a MutVar, which would have to be boxed
-- (writing to MutVar is slow because of GC book keeping)
next :: {-# UNPACK #-} !(U.MVector s Int32) -- ^ The next free index in the mutable store
-- The MVector is in a MutVar because grow is not in-place
, store :: {-# UNPACK #-} !(MutVar s (S.MVector s a)) -- ^ The mutable store
}
readU :: (Config, _) => _
writeU :: (Config, _) => _
writeM :: (Config, _) => _
readM :: (Config, _) => _
copyM :: (Config, _) => _
sliceM :: (Config, _) => _
getCount, getStackCount :: Config => PrimMonad m => VectorBuilder(PrimState m) a -> m Int32
#ifdef ENABLE_VECTOR_CHECKS
readU a = U.read a
writeU a = U.write a
writeM a ix = M.write a (fromIntegral ix)
readM a ix = M.read a (fromIntegral ix)
copyM a = M.copy a
sliceM a b = M.slice (fromIntegral a) (fromIntegral b)
#else
readU a = U.unsafeRead a
writeU a = U.unsafeWrite a
writeM a !ix = M.unsafeWrite a (fromIntegral ix)
readM a !ix = M.unsafeRead a (fromIntegral ix)
copyM a = M.unsafeCopy a
sliceM a b = M.unsafeSlice (fromIntegral a) (fromIntegral b)
#endif
-- | Yields the number of elements stored in the buffer
getCount l = readU (next l) 0
-- | Yields the number of elements in the stack
getStackCount l = readU (next l) 1
-- | Ensure that there is sufficient space
request :: (Config, Storable a, PrimMonad m) => Int32 -> VectorBuilder (PrimState m) a -> m ()
request !n VectorBuilder{next,store} = do
frontCount <- readU (next) 0
backCount <- readU (next) 1
assert (frontCount >= 0) $ return ()
assert (backCount >= 0) $ return ()
a <- readMutVar (store)
let !len = M.length a
unless (frontCount + backCount + n < fromIntegral len) $ do
return ()
a' <- newVectorAllocatedExternally(len*2)
copyM (sliceM 0 frontCount a') (sliceM 0 frontCount a)
copyM (sliceM (2 * fromIntegral len - backCount) backCount a') (sliceM (fromIntegral len - backCount) backCount a)
writeMutVar (store ) a'
{-# INLINE request #-}
-- | Inserts an element at the next free position in the vector and returns the index
insert :: (Config, PrimMonad m, Storable a) => VectorBuilder (PrimState m) a -> a -> m ()
insert l@VectorBuilder{next,store} v = do
request 1 l
nextI <- readU (next ) 0
writeU (next ) 0 (nextI + 1)
a <- readMutVar (store )
writeM a nextI v
{-# INLINE insert #-}
-- | Push an element into a stack-like temporary storage. Returns the index from the back
push :: (Config, PrimMonad m, Storable a) => VectorBuilder (PrimState m) a -> a -> m Int32
push l@VectorBuilder{next,store} !v = do
request 1 l
stackCount <- readU (next) 1
writeU (next) 1 (stackCount + 1)
assert (stackCount >= 0) $ return ()
a <- readMutVar (store)
let ix = (fromIntegral $ M.length a) - stackCount - 1
writeM a ix v
return stackCount
{-# INLINE push #-}
-- | Peek into the temporary stack
peek :: Config => (Storable a, PrimMonad m, Storable a, Num n, Integral n, Ord n) => VectorBuilder (PrimState m) a -> n -> m a
peek VectorBuilder{next, store} n = do
backCount <- readU next 1
assert(fromIntegral backCount > n) $ return ()
a <- readMutVar store
readM a (M.length a - fromIntegral backCount - 1 + fromIntegral n)
updateStackElt :: Config => (Storable a, PrimMonad m, Storable a) => VectorBuilder (PrimState m) a -> Int32 -> (a -> a) -> m ()
updateStackElt VectorBuilder{store} i f = do
arr <- readMutVar store
let ix = (M.length arr - fromIntegral i - 1)
a <- readM arr ix
writeM arr ix (f a)
{-# INLINE updateStackElt #-}
indexFromEnd :: forall a m . Config => (Storable a, PrimMonad m, Storable a) => VectorBuilder (PrimState m) a -> Int32 -> m (Ptr a)
indexFromEnd VectorBuilder{store} i = do
a <- readMutVar store
let (fptr, _) = SM.unsafeToForeignPtr0 a
let ptr = unsafeForeignPtrToPtr fptr
let s = sizeOf(undefined :: a)
return $ plusPtr ptr ((M.length a - fromIntegral i - 1) * s)
-- | Yield a short lived reference to the underlying foreign vector.
-- Note that Insert and Push may allocate a new underlying vector.
unsafeToMVector l = readMutVar (store l)
-- | Pop elements out of the temporary stack and append into the main buffer
pop :: Config => (Storable a, PrimMonad m, Storable a) => VectorBuilder (PrimState m) a -> Int32 -> m ()
pop l@VectorBuilder{next, store} n = do
request n l
backCount <- readU next 1
frontCount <- readU next 0
assert(backCount >= n) $ return ()
writeU next 0 (frontCount + n)
writeU next 1 (backCount - n)
a <- readMutVar store
let !l = M.length a
let !first = fromIntegral l - backCount
forM_ [1..n] $ \i ->
let from = frontCount - 1 + i
to = first+n-i
in when (from /= to) $
copyM (sliceM from 1 a) (sliceM to 1 a)
{-# INLINE pop #-}
-- | Extracts an immutable vector. The VectorBuilder can no longer be used after this
finalize :: (Config, PrimMonad m, Storable a) => VectorBuilder (PrimState m) a -> m (S.Vector a)
finalize l = trace "VectorBuilder.finalize" $ do
v <- readMutVar (store l)
frontCount <- readU (next l) 0
S.unsafeFreeze (sliceM 0 frontCount v)
new :: (Config, PrimMonad m, Storable a) => Int -> m (VectorBuilder (PrimState m) a)
new initialSize = do
next <- U.unsafeNew 2
writeU next 0 0
writeU next 1 0
store <- newVectorAllocatedExternally initialSize
storeRef <- newMutVar store
return (VectorBuilder next storeRef)
newVectorAllocatedExternally size = (`SM.unsafeFromForeignPtr0` size) <$> primToPrim (unsafeIOToST $ mallocForeignPtrArray size)