module Hans.Buffer.Datagram (
Buffer,
newBuffer,
writeChunk,
readChunk,
tryReadChunk,
isEmptyBuffer
) where
import Hans.Buffer.Signal
import Control.Monad (when)
import qualified Data.ByteString as S
import Data.IORef (IORef,newIORef,atomicModifyIORef',readIORef)
import qualified Data.Sequence as Seq
data Buffer a = Buffer { bufContents :: !(IORef (BufContents a))
, bufSignal :: !Signal
}
newBuffer :: Int -> IO (Buffer a)
newBuffer size =
do bufContents <- newIORef (emptyBufContents size)
bufSignal <- newSignal
return Buffer { .. }
writeChunk :: Buffer a -> a -> S.ByteString -> IO Bool
writeChunk Buffer { .. } a bytes =
do (written,more) <- atomicModifyIORef' bufContents (queueChunk a bytes)
when more (signal bufSignal)
return written
readChunk :: Buffer a -> IO (a,S.ByteString)
readChunk Buffer { .. } = loop
where
loop =
do mb <- atomicModifyIORef' bufContents dequeueChunk
case mb of
Just (c,more) ->
do when more (signal bufSignal)
return c
Nothing ->
do waitSignal bufSignal
loop
tryReadChunk :: Buffer a -> IO (Maybe (a,S.ByteString))
tryReadChunk Buffer { .. } =
do mb <- atomicModifyIORef' bufContents dequeueChunk
case mb of
Just (a,more) ->
do when more (signal bufSignal)
return (Just a)
Nothing ->
return Nothing
isEmptyBuffer :: Buffer a -> IO Bool
isEmptyBuffer Buffer { .. } =
(Seq.null . bufChunks) `fmap` readIORef bufContents
data BufContents a = BufContents { bufAvail :: !Int
, bufChunks :: !(Seq.Seq (a,S.ByteString))
}
emptyBufContents :: Int -> BufContents a
emptyBufContents bufAvail = BufContents { bufChunks = Seq.empty, .. }
chunksAvailable :: BufContents a -> Bool
chunksAvailable BufContents { .. } = not (Seq.null bufChunks)
queueChunk :: a -> S.ByteString -> BufContents a -> (BufContents a,(Bool,Bool))
queueChunk a chunk buf
| bufAvail buf >= chunkLen =
(BufContents { bufAvail = bufAvail buf chunkLen
, bufChunks = bufChunks buf Seq.|> (a,chunk) }, (True,True))
| otherwise =
(buf, (False,chunksAvailable buf))
where
chunkLen = S.length chunk
dequeueChunk :: BufContents a -> (BufContents a, Maybe ((a,S.ByteString),Bool))
dequeueChunk buf =
case Seq.viewl (bufChunks buf) of
c Seq.:< cs ->
let buf' = BufContents { bufAvail = bufAvail buf + S.length (snd c)
, bufChunks = cs }
in (buf', Just (c, chunksAvailable buf'))
_ -> (buf, Nothing)