-- | A concurrent, mutable ring buffer that supports atomic updates. This module is most efficient on buffers containing unboxed types.
module RingBuffers.Unlifted
  ( RingBuffer
  , new
  , clear
  , append
  , extend
  , capacity
  , filledLength
  , latest
  , unsafeLatest
  , foldMap
  , toList
  ) where

import qualified RingBuffers.Internal as I

-- | A concurrent, mutable ring buffer that supports atomic updates.
newtype RingBuffer a = RingBuffer (I.RingBuffer UnliftedArray a)

-- | Return a new ring buffer of the specified size.
new :: (PrimUnlifted a)
  => Int -- ^ capacity of buffer
  -> IO (RingBuffer a)
new :: Int -> IO (RingBuffer a)
new Int
sz = (RingBuffer UnliftedArray a -> RingBuffer a)
-> IO (RingBuffer UnliftedArray a) -> IO (RingBuffer a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap RingBuffer UnliftedArray a -> RingBuffer a
coerce (Int -> IO (RingBuffer UnliftedArray a)
forall (arr :: * -> *) a.
(Contiguous arr, Element arr a) =>
Int -> IO (RingBuffer arr a)
I.new Int
sz)

-- | Reset the buffer to its empty state.
clear :: (PrimUnlifted a)
  => RingBuffer a -- ^ buffer to clear
  -> IO ()
clear :: RingBuffer a -> IO ()
clear RingBuffer a
rb = RingBuffer UnliftedArray a -> IO ()
forall (arr :: * -> *) a.
(Contiguous arr, Element arr a) =>
RingBuffer arr a -> IO ()
I.clear (RingBuffer a -> RingBuffer UnliftedArray a
coerce RingBuffer a
rb)

-- | Get the current filled length of the ring
filledLength :: (PrimUnlifted a)
  => RingBuffer a
  -> IO Int
filledLength :: RingBuffer a -> IO Int
filledLength RingBuffer a
rb = RingBuffer UnliftedArray a -> IO Int
forall (arr :: * -> *) a.
(Contiguous arr, Element arr a) =>
RingBuffer arr a -> IO Int
I.filledLength (RingBuffer a -> RingBuffer UnliftedArray a
coerce RingBuffer a
rb)

-- | Get the maximum number of items the ring can contain
capacity :: (PrimUnlifted a)
  => RingBuffer a
  -> IO Int
capacity :: RingBuffer a -> IO Int
capacity RingBuffer a
rb = RingBuffer UnliftedArray a -> IO Int
forall (arr :: * -> *) a.
(Contiguous arr, Element arr a) =>
RingBuffer arr a -> IO Int
I.capacity (RingBuffer a -> RingBuffer UnliftedArray a
coerce RingBuffer a
rb)

-- | Retrieve the \(n\)th most-recently added item of the ring
latest :: (PrimUnlifted a)
  => RingBuffer a
  -> Int
  -> IO (Maybe a)
latest :: RingBuffer a -> Int -> IO (Maybe a)
latest RingBuffer a
rb Int
n = RingBuffer UnliftedArray a -> Int -> IO (Maybe a)
forall (arr :: * -> *) a.
(Contiguous arr, Element arr a) =>
RingBuffer arr a -> Int -> IO (Maybe a)
I.latest (RingBuffer a -> RingBuffer UnliftedArray a
coerce RingBuffer a
rb) Int
n

-- | Retrieve the \(n\)th most-recently added item of the ring
--
--   /Note/: This function may exhibit undefined behaviour if
--   the index is out-of-bounds or uninitialised.
unsafeLatest :: (PrimUnlifted a)
  => RingBuffer a
  -> Int
  -> IO a
unsafeLatest :: RingBuffer a -> Int -> IO a
unsafeLatest RingBuffer a
rb Int
n = RingBuffer UnliftedArray a -> Int -> IO a
forall (arr :: * -> *) a.
(Contiguous arr, Element arr a) =>
RingBuffer arr a -> Int -> IO a
I.unsafeLatest (RingBuffer a -> RingBuffer UnliftedArray a
coerce RingBuffer a
rb) Int
n

-- | Add an item to the end of the buffer.
append :: (PrimUnlifted a)
  => a
  -> RingBuffer a
  -> IO ()
append :: a -> RingBuffer a -> IO ()
append a
x RingBuffer a
rb = a -> RingBuffer UnliftedArray a -> IO ()
forall (arr :: * -> *) a.
(Contiguous arr, Element arr a) =>
a -> RingBuffer arr a -> IO ()
I.append a
x (RingBuffer a -> RingBuffer UnliftedArray a
coerce RingBuffer a
rb)

-- | Write multiple items to the end of the ring.
--
--   Ignores any elements of the input array whose indices
--   are higher than the length of the ring buffer.
extend :: (PrimUnlifted a)
  => UnliftedArray a
  -> RingBuffer a
  -> IO ()
extend :: UnliftedArray a -> RingBuffer a -> IO ()
extend UnliftedArray a
x RingBuffer a
rb = UnliftedArray a -> RingBuffer UnliftedArray a -> IO ()
forall (arr :: * -> *) a.
(Contiguous arr, Element arr a) =>
arr a -> RingBuffer arr a -> IO ()
I.extend UnliftedArray a
x (RingBuffer a -> RingBuffer UnliftedArray a
coerce RingBuffer a
rb)

-- | Execute the given action with the items of the ring, accumulating its results.
--
foldMap :: (PrimUnlifted a, Monoid b)
  => RingBuffer a
  -> (a -> IO b)
  -> IO b
foldMap :: RingBuffer a -> (a -> IO b) -> IO b
foldMap RingBuffer a
rb a -> IO b
action = RingBuffer UnliftedArray a -> (a -> IO b) -> IO b
forall (arr :: * -> *) a b.
(Contiguous arr, Element arr a, Monoid b) =>
RingBuffer arr a -> (a -> IO b) -> IO b
I.foldMap (RingBuffer a -> RingBuffer UnliftedArray a
coerce RingBuffer a
rb) a -> IO b
action

-- | Convert the entire contents of the ring into a list,
--   with the most recently added element at the head.
toList :: (PrimUnlifted a)
  => RingBuffer a
  -> IO [a]
toList :: RingBuffer a -> IO [a]
toList RingBuffer a
rb = RingBuffer UnliftedArray a -> IO [a]
forall (arr :: * -> *) a.
(Contiguous arr, Element arr a) =>
RingBuffer arr a -> IO [a]
I.toList (RingBuffer a -> RingBuffer UnliftedArray a
coerce RingBuffer a
rb)