module Data.RingBuffer ( RingBuffer
, new
, clear
, append
, concat
, capacity
, length
, latest
, toList
, withItems
) where
import qualified Data.Vector.Generic as VG
import qualified Data.Vector.Generic.Mutable as VGM
import Control.Applicative
import Control.Concurrent
import Control.Monad.Catch
import Control.Monad.State
import Control.Monad.Reader
import Control.Monad.Primitive
import Prelude hiding (length, concat)
data RingBuffer v a
= RingBuffer { ringBuffer :: (VG.Mutable v) (PrimState IO) a
, ringState :: MVar RingState
}
data RingState = RingState { ringFull :: !Bool
, ringHead :: !Int
}
type RingM m vm a = StateT RingState (ReaderT (vm (PrimState IO) a) m)
withRing :: (VG.Vector v a, MonadIO m, MonadMask m)
=> RingBuffer v a
-> RingM m (VG.Mutable v) a r
-> m r
withRing rb action = mask_ $ do
s <- liftIO $ takeMVar (ringState rb)
(r, s') <- runReaderT (runStateT action s) (ringBuffer rb)
liftIO $ putMVar (ringState rb) s'
return r
advance :: (VGM.MVector v a, MonadIO m) => Int -> RingM m v a ()
advance n = do
RingState full pos <- get
cap <- capacity'
let (a, pos') = (pos + n) `divMod` cap
put $ RingState (full || a > 0) pos'
new :: (VG.Vector v a) => Int -> IO (RingBuffer v a)
new n = do
when (n < 1) $ fail "Data.RingBuffer.new: invalid ring size"
buffer <- VGM.new n
state0 <- newMVar $ RingState False 0
return $ RingBuffer { ringBuffer=buffer, ringState=state0 }
clear :: VG.Vector v a => RingBuffer v a -> IO ()
clear rb = withRing rb $ put $ RingState False 0
append :: (VG.Vector v a) => a -> RingBuffer v a -> IO ()
append x rb = withRing rb $ do
s <- get
liftIO $ VGM.unsafeWrite (ringBuffer rb) (ringHead s) x
advance 1
concat :: (VG.Vector v a) => v a -> RingBuffer v a -> IO ()
concat xs rb = withRing rb $ do
cap <- capacity'
let takeN = min (VG.length xs) cap
xs' <- liftIO $ VG.unsafeThaw $ VG.drop (VG.length xs takeN) xs
pos <- gets ringHead
let untilWrap = cap pos
src = VGM.take untilWrap xs'
dest = VGM.take (min takeN untilWrap) $ VGM.drop pos $ ringBuffer rb
liftIO $ VGM.copy dest src
when (takeN > untilWrap) $ do
let src' = VGM.drop untilWrap xs'
dest' = VGM.take (takeN untilWrap) $ ringBuffer rb
liftIO $ VGM.copy dest' src'
advance takeN
capacity :: (VG.Vector v a) => RingBuffer v a -> Int
capacity rb = VGM.length (ringBuffer rb)
capacity' :: (VGM.MVector v a, MonadIO m) => RingM m v a Int
capacity' = asks VGM.length
length' :: (VGM.MVector v a, MonadIO m) => RingM m v a Int
length' = do
RingState full pos <- get
if full
then capacity'
else return pos
length :: (VG.Vector v a) => RingBuffer v a -> IO Int
length rb = withRing rb length'
latest :: (VG.Vector v a) => RingBuffer v a -> Int -> IO (Maybe a)
latest rb n = withRing rb $ do
len <- length'
if n >= len
then return Nothing
else Just <$> latest' n
latest' :: (VGM.MVector v a, MonadIO m) => Int -> RingM m v a a
latest' n = do
len <- length'
cap <- capacity'
when (n >= len) $ error "Data.RingBuffer.latest': invalid index"
RingState _ hd <- get
let idx = (hd n 1) `mod` cap
buf <- ask
liftIO $ VGM.unsafeRead buf idx
toList :: (VG.Vector v a) => RingBuffer v a -> IO [a]
toList rb = withRing rb $ do
len <- length'
mapM latest' [0..len1]
withItems :: (MonadIO m, MonadMask m, VG.Vector v a)
=> RingBuffer v a -> (v a -> m b) -> m b
withItems rb action = withRing rb $ do
frozen <- liftIO $ VG.unsafeFreeze (ringBuffer rb)
n <- length'
lift $ lift $ action (VG.take n frozen)