{-|
Unboxed mutable references backed by unboxed vectors.
-}

module Data.STRef.Unboxed
  ( -- * Unboxed ST References
    STRefU(..)
  , newSTRefU
  , readSTRefU
  , writeSTRefU
  , modifySTRefU
  ) where

import           Control.Monad.ST
import           Data.Coerce
import           Data.Vector.Unboxed.Mutable (MVector, Unbox)
import qualified Data.Vector.Unboxed.Mutable as Unboxed


-- |
-- An unboxed mutable variable that can hold any type which is
-- an instance of 'Unbox'. This is backed by a length 1 unboxed
-- mutable vector.
newtype STRefU s a = STRefU {STRefU s a -> MVector s a
getSTRefU :: MVector s a}


-- |
-- Create a new `STRefU` containing the given value.
newSTRefU :: (Unbox a) => a -> ST s (STRefU s a)
newSTRefU :: a -> ST s (STRefU s a)
newSTRefU a :: a
a =
  do
    MVector s a
mv <- Int -> ST s (MVector (PrimState (ST s)) a)
forall (m :: * -> *) a.
(PrimMonad m, Unbox a) =>
Int -> m (MVector (PrimState m) a)
Unboxed.unsafeNew 1
    MVector (PrimState (ST s)) a -> Int -> a -> ST s ()
forall (m :: * -> *) a.
(PrimMonad m, Unbox a) =>
MVector (PrimState m) a -> Int -> a -> m ()
Unboxed.write MVector s a
MVector (PrimState (ST s)) a
mv 0 a
a
    STRefU s a -> ST s (STRefU s a)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (MVector s a -> STRefU s a
forall a b. Coercible a b => a -> b
coerce MVector s a
mv)
-- |
-- Read the value in a reference cell.
readSTRefU :: (Unbox a) => STRefU s a -> ST s a
readSTRefU :: STRefU s a -> ST s a
readSTRefU (STRefU mv :: MVector s a
mv) = MVector (PrimState (ST s)) a -> Int -> ST s a
forall (m :: * -> *) a.
(PrimMonad m, Unbox a) =>
MVector (PrimState m) a -> Int -> m a
Unboxed.read MVector s a
MVector (PrimState (ST s)) a
mv 0


-- |
-- Write a value to a reference cell.
writeSTRefU :: (Unbox a) => STRefU s a -> a -> ST s ()
writeSTRefU :: STRefU s a -> a -> ST s ()
writeSTRefU (STRefU mv :: MVector s a
mv) = MVector (PrimState (ST s)) a -> Int -> a -> ST s ()
forall (m :: * -> *) a.
(PrimMonad m, Unbox a) =>
MVector (PrimState m) a -> Int -> a -> m ()
Unboxed.write MVector s a
MVector (PrimState (ST s)) a
mv 0


-- |
-- Modify the value in a reference cell. This is strict in its argument.
modifySTRefU :: (Unbox a) => STRefU s a -> (a -> a) -> ST s ()
modifySTRefU :: STRefU s a -> (a -> a) -> ST s ()
modifySTRefU (STRefU mv :: MVector s a
mv) f :: a -> a
f = MVector (PrimState (ST s)) a -> (a -> a) -> Int -> ST s ()
forall (m :: * -> *) a.
(PrimMonad m, Unbox a) =>
MVector (PrimState m) a -> (a -> a) -> Int -> m ()
Unboxed.modify MVector s a
MVector (PrimState (ST s)) a
mv a -> a
f 0