{-# LANGUAGE DuplicateRecordFields #-}

{- | Data types for describing an array paired with an index
into it. This is intended to be used in wrappers for @unsafe@
FFI calls. For example, the POSIX function @recvfrom@ takes
a @socklen_t*@ argument. (Let us assume that @socklen_t@
is equivalant to @int@ for this example.) How is this argument best
described by a Haskell type? When working with pinned memory,
the best option @'Ptr' 'CInt'@.
It works equally well regardless of whether we originally had an
array of 'CInt' or a pointer to a single 'CInt'. This works because
of functions like 'Data.Primitive.advancePtr' and
'Foreign.Ptr.plusPtr' that effectively index into an array.
Unpinned memory, however, is trickier. We want to have the full
flexibility (handling both a single-element or
multi-element buffer) that @'Ptr' 'CInt'@ affords. We cannot
offset into a 'MutablePrimArray' to get a new one like we
could with 'Ptr'. (Such a function is not possible because
unpinned memory can be relocated.) So, the offseting must
be done in the C function wrapped by the @unsafe@ FFI. This
means that the offset must be passed together with the
'MutablePrimArray'. This is the precisely the product that
'MutablePrimArrayOffset' represents. In a type signature, it
provides additional clarity about the meaning of the offset.

This library is used in the extensively in the @posix-api@
library to clarify intent in a number of type signatures.
-}
module Data.Primitive.PrimArray.Offset
  ( -- * Types
    PrimArrayOffset (..)
  , MutablePrimArrayOffset (..)

    -- * Resolution
  , indexOffset
  , readOffset
  ) where

import Control.Monad.Primitive (PrimMonad, PrimState)
import Data.Primitive (MutablePrimArray, Prim, PrimArray, indexPrimArray, readPrimArray)

-- | A primitive array and an index into the array.
data PrimArrayOffset a = PrimArrayOffset
  { forall a. PrimArrayOffset a -> PrimArray a
array :: {-# UNPACK #-} !(PrimArray a)
  , forall a. PrimArrayOffset a -> Int
offset :: {-# UNPACK #-} !Int
  }

-- | A mutable primitive array and an index into the array.
data MutablePrimArrayOffset s a = MutablePrimArrayOffset
  { forall s a. MutablePrimArrayOffset s a -> MutablePrimArray s a
array :: {-# UNPACK #-} !(MutablePrimArray s a)
  , forall s a. MutablePrimArrayOffset s a -> Int
offset :: {-# UNPACK #-} !Int
  }

-- | Recover the element in the primitive array.
indexOffset ::
  (Prim a) =>
  PrimArrayOffset a ->
  a
{-# INLINE indexOffset #-}
indexOffset :: forall a. Prim a => PrimArrayOffset a -> a
indexOffset (PrimArrayOffset PrimArray a
arr Int
ix) = PrimArray a -> Int -> a
forall a. Prim a => PrimArray a -> Int -> a
indexPrimArray PrimArray a
arr Int
ix

-- | Recover the element in the mutable primitive array.
readOffset ::
  (PrimMonad m, Prim a) =>
  MutablePrimArrayOffset (PrimState m) a ->
  m a
{-# INLINE readOffset #-}
readOffset :: forall (m :: * -> *) a.
(PrimMonad m, Prim a) =>
MutablePrimArrayOffset (PrimState m) a -> m a
readOffset (MutablePrimArrayOffset MutablePrimArray (PrimState m) a
arr Int
ix) = MutablePrimArray (PrimState m) a -> Int -> m a
forall a (m :: * -> *).
(Prim a, PrimMonad m) =>
MutablePrimArray (PrimState m) a -> Int -> m a
readPrimArray MutablePrimArray (PrimState m) a
arr Int
ix