{-# LANGUAGE NoImplicitPrelude #-}
module RIO.Deque
(
Deque
, UDeque
, SDeque
, BDeque
, newDeque
, getDequeSize
, popFrontDeque
, popBackDeque
, pushFrontDeque
, pushBackDeque
, foldlDeque
, foldrDeque
, dequeToList
, dequeToVector
, freezeDeque
, asUDeque
, asSDeque
, asBDeque
) where
import RIO.Prelude.Reexports
import Control.Exception (assert)
import Control.Monad (liftM)
import qualified Data.Vector.Generic as VG
import qualified Data.Vector.Generic.Mutable as V
import qualified Data.Vector.Mutable as B
import qualified Data.Vector.Storable.Mutable as S
import qualified Data.Vector.Unboxed.Mutable as U
import Data.Primitive.MutVar
data DequeState v s a = DequeState
!(v s a)
{-# UNPACK #-} !Int
{-# UNPACK #-} !Int
newtype Deque v s a = Deque (MutVar s (DequeState v s a))
type UDeque = Deque U.MVector
type SDeque = Deque S.MVector
type BDeque = Deque B.MVector
asUDeque :: UDeque s a -> UDeque s a
asUDeque = id
asSDeque :: SDeque s a -> SDeque s a
asSDeque = id
asBDeque :: BDeque s a -> BDeque s a
asBDeque = id
newDeque
:: (V.MVector v a, PrimMonad m)
=> m (Deque v (PrimState m) a)
newDeque = do
v <- V.new baseSize
liftM Deque $ newMutVar (DequeState v 0 0)
where
baseSize = 32
{-# INLINE newDeque #-}
getDequeSize :: PrimMonad m => Deque v (PrimState m) a -> m Int
getDequeSize (Deque var) = do
DequeState _ _ size <- readMutVar var
pure size
{-# INLINE getDequeSize #-}
popFrontDeque
:: (V.MVector v a, PrimMonad m)
=> Deque v (PrimState m) a
-> m (Maybe a)
popFrontDeque (Deque var) = do
DequeState v start size <- readMutVar var
if size == 0
then return Nothing
else do
x <- V.unsafeRead v start
let start' = start + 1
start''
| start' >= V.length v = 0
| otherwise = start'
writeMutVar var $! DequeState v start'' (size - 1)
return $! Just x
{-# INLINE popFrontDeque #-}
popBackDeque
:: (V.MVector v a, PrimMonad m)
=> Deque v (PrimState m) a
-> m (Maybe a)
popBackDeque (Deque var) = do
DequeState v start size <- readMutVar var
if size == 0
then return Nothing
else do
let size' = size - 1
end = start + size'
end'
| end >= V.length v = end - V.length v
| otherwise = end
x <- V.unsafeRead v end'
writeMutVar var $! DequeState v start size'
return $! Just x
{-# INLINE popBackDeque #-}
pushFrontDeque
:: (V.MVector v a, PrimMonad m)
=> Deque v (PrimState m) a
-> a
-> m ()
pushFrontDeque (Deque var) x = do
DequeState v start size <- readMutVar var
inner v start size
where
inner v start size = do
if size >= V.length v
then newVector v start size inner
else do
let size' = size + 1
start' = (start - 1) `rem` V.length v
start''
| start' < 0 = V.length v + start'
| otherwise = start'
V.unsafeWrite v start'' x
writeMutVar var $! DequeState v start'' size'
{-# INLINE pushFrontDeque #-}
pushBackDeque
:: (V.MVector v a, PrimMonad m)
=> Deque v (PrimState m) a
-> a
-> m ()
pushBackDeque (Deque var) x = do
DequeState v start size <- readMutVar var
inner v start size
where
inner v start size = do
if size >= V.length v
then newVector v start size inner
else do
let end = start + size
end'
| end >= V.length v = end - V.length v
| otherwise = end
V.unsafeWrite v end' x
writeMutVar var $! DequeState v start (size + 1)
{-# INLINE pushBackDeque #-}
foldlDeque
:: (V.MVector v a, PrimMonad m)
=> (acc -> a -> m acc)
-> acc
-> Deque v (PrimState m) a
-> m acc
foldlDeque f acc0 (Deque var) = do
DequeState v start size <- readMutVar var
let loop idx acc
| idx >= size = pure acc
| otherwise = do
let idxPlusStart = idx + start
idx'
| idxPlusStart >= V.length v = idxPlusStart - V.length v
| otherwise = idxPlusStart
a <- V.unsafeRead v idx'
acc' <- f acc a
loop (idx + 1) $! acc'
loop 0 acc0
foldrDeque
:: (V.MVector v a, PrimMonad m)
=> (a -> acc -> m acc)
-> acc
-> Deque v (PrimState m) a
-> m acc
foldrDeque f acc0 (Deque var) = do
DequeState v start size <- readMutVar var
let loop idx acc
| idx < 0 = pure acc
| otherwise = do
let idxPlusStart = idx + start
idx'
| idxPlusStart >= V.length v = idxPlusStart - V.length v
| otherwise = idxPlusStart
a <- V.unsafeRead v idx'
acc' <- f a acc
loop (idx - 1) $! acc'
loop (size - 1) acc0
dequeToList
:: (V.MVector v a, PrimMonad m)
=> Deque v (PrimState m) a
-> m [a]
dequeToList = foldrDeque (\a rest -> pure $ a : rest) []
{-# INLINE dequeToList #-}
dequeToVector :: (VG.Vector v' a, V.MVector v a, PrimMonad m)
=> Deque v (PrimState m) a -> m (v' a)
dequeToVector dq = do
size <- getDequeSize dq
mv <- V.unsafeNew size
foldlDeque (\i e -> V.unsafeWrite mv i e >> pure (i+1)) 0 dq
VG.unsafeFreeze mv
newVector :: (PrimMonad m, V.MVector v a)
=> v (PrimState m) a
-> Int
-> Int
-> (v (PrimState m) a -> Int -> Int -> m b)
-> m b
newVector v size2 sizeOrig f = assert (sizeOrig == V.length v) $ do
v' <- V.unsafeNew (V.length v * 2)
let size1 = V.length v - size2
V.unsafeCopy
(V.unsafeTake size1 v')
(V.unsafeSlice size2 size1 v)
V.unsafeCopy
(V.unsafeSlice size1 size2 v')
(V.unsafeTake size2 v)
f v' 0 sizeOrig
{-# INLINE newVector #-}
freezeDeque ::
(VG.Vector v a, PrimMonad m)
=> Deque (VG.Mutable v) (PrimState m) a
-> m (v a)
freezeDeque (Deque var) = do
state@(DequeState v _ size) <- readMutVar var
v' <- V.unsafeNew size
makeCopy v' state
VG.unsafeFreeze v'
makeCopy ::
(V.MVector v a, PrimMonad m)
=> v (PrimState m) a
-> DequeState v (PrimState m) a
-> m ()
makeCopy v' (DequeState v start size) = do
let size1 = min size (V.length v - start)
size2 = size - size1
V.unsafeCopy
(V.unsafeTake size1 v')
(V.unsafeSlice start size1 v)
when (size > size1) $ V.unsafeCopy
(V.unsafeSlice size1 size2 v')
(V.unsafeTake size2 v)
{-# INLINE makeCopy #-}