{-# 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
  , MutablePrimArrayOffset(..)
    -- * Resolution
  , indexOffset
  , readOffset
  ) where

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

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

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

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

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