packages feed

mcl-1.0.0: src/MCL/Internal/Prim/Pinned.hs

{-# LANGUAGE MagicHash #-}
{-# LANGUAGE ScopedTypeVariables #-}
module MCL.Internal.Prim.Pinned where

import Data.Functor.Identity
import Data.Primitive.ByteArray
import Foreign.C.Types
import GHC.Exts
import System.IO.Unsafe

import MCL.Internal.Prim.Class
import MCL.Internal.Utils

-- | Pass pinned copy of the object to a safe C function.
{-# INLINABLE withPrimPinned #-}
withPrimPinned :: Prim t => t -> (CC t -> IO r) -> IO r
withPrimPinned fp k = do
  ByteArray ba <- pinnedByteArrayCopy $ ByteArray (prim_unwrap fp)
  k ba
  where
    pinnedByteArrayCopy :: ByteArray -> IO ByteArray
    pinnedByteArrayCopy src = do
      mdest <- newPinnedByteArray srcSize
      copyByteArray mdest 0 src 0 srcSize
      unsafeFreezeByteArray mdest
      where
        srcSize = sizeofByteArray src

-- | Use pinned memory to get a result from a safe C function.
{-# INLINABLE newPrimPinned #-}
newPrimPinned
  :: forall m t r. Prim t
  => (r -> CC t -> m t)
  -> (MC t -> IO r)
  -> IO (m t)
newPrimPinned = withPinnedByteArray1 (prim_size (proxy# :: Proxy# t))

{-# INLINABLE newPrimPinned_ #-}
newPrimPinned_ :: forall t. Prim t => (MC t -> IO ()) -> IO t
newPrimPinned_ = unwrap . newPrimPinned (\_ ba -> Identity (prim_wrap ba))
  where
    unwrap :: IO (Identity t) -> IO t
    unwrap = coerce

{-# INLINABLE maybeNewPrimPinned #-}
maybeNewPrimPinned :: Prim t => (MC t -> IO CInt) -> IO (Maybe t)
maybeNewPrimPinned = newPrimPinned $ \success ba ->
  if cintToBool success
  then Just (prim_wrap ba)
  else Nothing

----------------------------------------

{-# INLINABLE safeOp0 #-}
safeOp0 :: IO r -> r
safeOp0 = unsafeDupablePerformIO

{-# INLINABLE safeOp1 #-}
safeOp1
  :: Prim t
  => (s -> IO r)
  -> (CC t -> s)
  -> (t -> r)
safeOp1 k c_fun fa =
  safeOp0 . withPrimPinned fa $ \a -> k (c_fun a)

{-# INLINABLE safeOp1_ #-}
safeOp1_
  :: (Prim t, Prim r)
  => (CC t -> MC r -> IO ())
  -> (t -> r)
safeOp1_ = safeOp1 newPrimPinned_

{-# INLINABLE safeOp2 #-}
safeOp2
  :: (Prim t1, Prim t2)
  => (s -> IO r)
  -> (CC t1 -> CC t2 -> s)
  -> (t1 -> t2 -> r)
safeOp2 k c_fun fa fb =
  safeOp0 . withPrimPinned fa $ \a -> withPrimPinned fb $ \b -> k (c_fun a b)

{-# INLINABLE safeOp2_ #-}
safeOp2_
  :: (Prim t1, Prim t2, Prim r)
  => (CC t1 -> CC t2 -> MC r -> IO ())
  -> (t1 -> t2 -> r)
safeOp2_ = safeOp2 newPrimPinned_

----------------------------------------
-- Utils

{-# INLINABLE withPinnedByteArray1 #-}
withPinnedByteArray1
  :: Int
  -> (s -> ByteArray# -> r)
  -> (MutableByteArray# RealWorld -> IO s)
  -> IO r
withPinnedByteArray1 size k c_fun = do
  mba@(MutableByteArray umba) <- newPinnedByteArray size
  r <- c_fun umba
  ByteArray uba <- unpinnedFreezeByteArray mba
  return (k r uba)
  where
    unpinnedFreezeByteArray :: MutableByteArray RealWorld -> IO ByteArray
    unpinnedFreezeByteArray src = do
      mdest <- newByteArray srcSize
      copyMutableByteArray mdest 0 src 0 srcSize
      unsafeFreezeByteArray mdest
        where
          srcSize = sizeofMutableByteArray src