{-# OPTIONS_GHC -w #-} {-# LANGUAGE BangPatterns #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE TypeFamilies #-} module Data.Vector.Dynamic ( DynamicVector , newDyn , newDyn' , readDyn , writeDyn , pushDyn , popDyn , shiftDyn , unshiftDyn , sizeDyn , toListDyn ) where import Control.Imperative.Vector.Base (VectorElem, VectorEntity) import Control.Monad import Control.Monad.Primitive (PrimMonad, PrimState) import Data.Bits import Data.Int import Data.Primitive.MutVar import qualified Data.Vector.Generic.Mutable as GMV import qualified Data.Vector.Mutable as MV import qualified Data.Vector.Unboxed as UV import Data.Word data DynamicVector m a = Dynamic { logSizeVar :: MutVar (PrimState m) Int , frontVar :: MutVar (PrimState m) Int , rearVar :: MutVar (PrimState m) Int , vecVar :: MutVar (PrimState m) (VectorEntity a (PrimState m) a) } toPow2 :: Int -> Int toPow2 n | n <= 0 = 1 | otherwise = go (n-1) 1 where go 0 !c = c go n !c = go (shiftR n 1) (shiftL c 1) {-# INLINE toPow2 #-} newDyn :: (PrimMonad m, VectorElem a) => Int -> m (DynamicVector m a) newDyn logSize = liftM4 Dynamic (newMutVar logSize) (newMutVar 0) (newMutVar (max 0 (logSize-1))) (GMV.unsafeNew (toPow2 logSize) >>= newMutVar) {-# INLINE newDyn #-} newDyn' :: (PrimMonad m, VectorElem a) => Int -> a -> m (DynamicVector m a) newDyn' logSize x = do d@(Dynamic {vecVar}) <- newDyn logSize v <- readMutVar vecVar w <- GMV.replicate logSize x unsafeMemCopy 0 v 0 (max 0 logSize) w return d {-# INLINE newDyn' #-} unsafeMemCopy :: (PrimMonad m, GMV.MVector v a) => Int -- dst offset -> v (PrimState m) a -- dst -> Int -- src offset -> Int -- length -> v (PrimState m) a -- src -> m () unsafeMemCopy i dst j n src = do let sdst = GMV.unsafeSlice i n dst ssrc = GMV.unsafeSlice j n src GMV.unsafeCopy sdst ssrc {-# INLINE unsafeMemCopy #-} readDyn :: (PrimMonad m, VectorElem a) => DynamicVector m a -> Int -> m a readDyn d@(Dynamic {logSizeVar, frontVar, rearVar, vecVar}) logIndex = do logSize <- readMutVar logSizeVar when (logSize <= logIndex || logIndex < 0) $ error $ "index out of bounds " ++ show (logIndex, logSize) phyIndex <- log2phy d logIndex vec <- readMutVar vecVar GMV.unsafeRead vec phyIndex {-# INLINE readDyn #-} log2phy :: (PrimMonad m, VectorElem a) => DynamicVector m a -> Int -> m Int log2phy (Dynamic {frontVar, vecVar}) i = do front <- readMutVar frontVar vec <- readMutVar vecVar let realCap = GMV.length vec return $ (front + i) .&. (realCap - 1) {-# INLINE log2phy #-} writeDyn :: (PrimMonad m, VectorElem a) => DynamicVector m a -> Int -> a -> m () writeDyn d@(Dynamic {logSizeVar, rearVar, vecVar}) logIndex x = do logSize <- readMutVar logSizeVar when (logIndex < 0) $ error $ "index out of bounds " ++ show (logIndex, logSize) when (logSize <= logIndex) $ do resizeDyn d (logIndex + 1) log2phy d logIndex >>= writeMutVar rearVar phyIndex <- log2phy d logIndex vec <- readMutVar vecVar GMV.unsafeWrite vec phyIndex x {-# INLINE writeDyn #-} resizeDyn :: (PrimMonad m, VectorElem a) => DynamicVector m a -> Int -> m () resizeDyn (Dynamic {logSizeVar, frontVar, rearVar, vecVar}) newLogSize = do vec <- readMutVar vecVar let realCap = GMV.length vec when (realCap < newLogSize) $ do let newRealCap = until (newLogSize <=) (*2) realCap diff = newRealCap - realCap front <- readMutVar frontVar rear <- readMutVar rearVar newVec <- if front > rear then do let n = realCap - front nv <- GMV.unsafeNew newRealCap unsafeMemCopy 0 nv 0 (rear+1) vec unsafeMemCopy (front + diff) nv front n vec writeMutVar frontVar (front + diff) return nv else GMV.unsafeGrow vec diff writeMutVar vecVar newVec writeMutVar logSizeVar newLogSize {-# INLINE resizeDyn #-} pushDyn :: (PrimMonad m, VectorElem a) => DynamicVector m a -> a -> m () pushDyn d@(Dynamic {logSizeVar, rearVar}) x = do logSize <- readMutVar logSizeVar writeDyn d logSize x {-# INLINE pushDyn #-} popDyn :: (PrimMonad m, VectorElem a) => DynamicVector m a -> m a popDyn d@(Dynamic {logSizeVar, frontVar, rearVar}) = do logSize <- readMutVar logSizeVar when (logSize < 1) $ error "Couldn't pop on an empty vector" x <- readDyn d (logSize-1) when (logSize > 1) $ log2phy d (logSize-2) >>= writeMutVar rearVar modifyMutVar' logSizeVar pred return x {-# INLINE popDyn #-} shiftDyn :: (PrimMonad m, VectorElem a) => DynamicVector m a -> m a shiftDyn d@(Dynamic {logSizeVar, frontVar, rearVar, vecVar}) = do logSize <- readMutVar logSizeVar when (logSize < 1) $ error "Couldn't shift on an empty vector" x <- readDyn d 0 when (logSize > 1) $ log2phy d 1 >>= writeMutVar frontVar modifyMutVar' logSizeVar pred return x {-# INLINE shiftDyn #-} unshiftDyn :: (PrimMonad m, VectorElem a) => DynamicVector m a -> a -> m () unshiftDyn d@(Dynamic {logSizeVar, frontVar, rearVar, vecVar}) x = do logSize <- readMutVar logSizeVar realCap <- liftM GMV.length $ readMutVar vecVar if logSize == realCap then resizeDyn d (logSize+1) else modifyMutVar' logSizeVar succ phyNewHead <- log2phy d (-1) vec <- readMutVar vecVar GMV.unsafeWrite vec phyNewHead x writeMutVar frontVar phyNewHead when (logSize==0) $ writeMutVar rearVar phyNewHead {-# INLINE unshiftDyn #-} toListDyn :: (PrimMonad m, VectorElem a) => DynamicVector m a -> m [a] toListDyn (Dynamic {logSizeVar, frontVar, rearVar, vecVar}) = do logSize <- readMutVar logSizeVar if logSize == 0 then return [] else do front <- readMutVar frontVar rear <- readMutVar rearVar vec <- readMutVar vecVar let realCap = GMV.length vec indicies = if front <= rear then [front..rear] else [front..realCap-1] ++ [0..rear] forM indicies $ GMV.unsafeRead vec {-# INLINE toListDyn #-} sizeDyn :: PrimMonad m => DynamicVector m a -> m Int sizeDyn (Dynamic {logSizeVar}) = readMutVar logSizeVar {-# INLINE sizeDyn #-}