-- |
-- Module      : Streamly.Internal.Data.Ring
-- Copyright   : (c) 2021 Composewell Technologies
-- License     : BSD-3-Clause
-- Maintainer  : streamly@composewell.com
-- Stability   : experimental
-- Portability : GHC
--

module Streamly.Internal.Data.Ring
    ( Ring(..)
    , createRing
    , unsafeInsertRing
    ) where

import Control.Monad.Primitive (PrimMonad(PrimState))
import Data.IORef (modifyIORef', newIORef, readIORef, writeIORef, IORef)
import Data.Primitive.Array (newArray, writeArray, MutableArray)

data Ring a = Ring
    { forall a. Ring a -> MutableArray (PrimState IO) a
arr :: MutableArray (PrimState IO) a
    , forall a. Ring a -> IORef Int
ringHead :: IORef Int -- current index to be over-written
    , forall a. Ring a -> Int
ringMax :: !Int       -- first index beyond allocated memory
    }

{-# INLINE createRing #-}
createRing :: Int -> IO (Ring a)
createRing :: forall a. Int -> IO (Ring a)
createRing Int
count = do
    MutableArray RealWorld a
arr' <- forall (m :: * -> *) a.
PrimMonad m =>
Int -> a -> m (MutableArray (PrimState m) a)
newArray Int
count (forall a. HasCallStack => a
undefined :: a)
    IORef Int
head' <- forall a. a -> IO (IORef a)
newIORef Int
0
    forall (m :: * -> *) a. Monad m => a -> m a
return (Ring
        { arr :: MutableArray (PrimState IO) a
arr = MutableArray RealWorld a
arr'
        , ringHead :: IORef Int
ringHead = IORef Int
head'
        , ringMax :: Int
ringMax = Int
count
        })

{-# INLINE unsafeInsertRing #-}
unsafeInsertRing :: Ring a -> Int -> a -> IO ()
unsafeInsertRing :: forall a. Ring a -> Int -> a -> IO ()
unsafeInsertRing Ring{Int
IORef Int
MutableArray (PrimState IO) a
ringMax :: Int
ringHead :: IORef Int
arr :: MutableArray (PrimState IO) a
ringMax :: forall a. Ring a -> Int
ringHead :: forall a. Ring a -> IORef Int
arr :: forall a. Ring a -> MutableArray (PrimState IO) a
..} Int
idx a
x = do
    forall (m :: * -> *) a.
PrimMonad m =>
MutableArray (PrimState m) a -> Int -> a -> m ()
writeArray MutableArray (PrimState IO) a
arr (forall a. Integral a => a -> a -> a
mod Int
idx Int
ringMax) a
x
    Int
ref <- forall a. IORef a -> IO a
readIORef IORef Int
ringHead
    if (Int
refforall a. Num a => a -> a -> a
+Int
1) forall a. Ord a => a -> a -> Bool
< Int
ringMax
    then forall a. IORef a -> (a -> a) -> IO ()
modifyIORef' IORef Int
ringHead ( forall a. Num a => a -> a -> a
+ Int
1)
    else forall a. IORef a -> a -> IO ()
writeIORef IORef Int
ringHead Int
0