packages feed

foreign-0.1.0.0: src/HsForeign/Primitive.hs

{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE CPP          #-}
{-# LANGUAGE MagicHash    #-}

module HsForeign.Primitive
  ( BA# (BA#)
  , MBA# (MBA#)
  , BAArray# (BAArray#)
  , withPrim, allocPrim
  , withPrimUnsafe
  , allocPrimUnsafe
  , withPrimArray
  , allocPrimArray
  , withPrimArrayUnsafe
  , allocPrimArrayUnsafe
  , withPrimArrayList
  , withPrimArrayListUnsafe
  , withForeignPtrList

    -- * Internal helpers
  , withMutablePrimArrayContents
  , withPrimArrayContents
  , byteArrayContents#
  , mutableByteArrayContents#

    -- * Re-export
  , module Data.Primitive
  , module Control.Monad.Primitive
  ) where

import           Control.Monad                 (foldM_)
import           Control.Monad.Primitive
import           Data.Primitive
import           Data.Primitive.Unlifted.Array
import           Foreign.ForeignPtr
import           GHC.Exts

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

newtype BA# a = BA# ByteArray#
newtype MBA# a = MBA# (MutableByteArray# RealWorld)
newtype BAArray# a = BAArray# ArrayArray#

-- From Z-Data package: Z.Foreign
--
-- | Create an one element primitive array and use it as a pointer to the
-- primitive element.
--
-- Don't pass a forever loop to this function,
-- see <https://ghc.haskell.org/trac/ghc/ticket/14346 #14346>.
withPrim :: forall a b. Prim a => a -> (Ptr a -> IO b) -> IO (a, b)
withPrim v f = do
  buf <- newAlignedPinnedPrimArray 1
  writePrimArray buf 0 v
  !b <- withMutablePrimArrayContents buf $ \ ptr -> f ptr
  !a <- readPrimArray buf 0
  return (a, b)
{-# INLINABLE withPrim #-}

-- From Z-Data package: Z.Foreign
--
-- | like 'withPrim', but don't write initial value.
allocPrim :: forall a b. Prim a => (Ptr a -> IO b) -> IO (a, b)
allocPrim f = do
  buf <- newAlignedPinnedPrimArray 1
  !b <- withMutablePrimArrayContents buf $ \ ptr -> f ptr
  !a <- readPrimArray buf 0
  return (a, b)
{-# INLINABLE allocPrim #-}

-- From Z-Data package: Z.Foreign
--
-- | Create an one element primitive array and use it as a pointer to the
-- primitive element.
--
-- Return the element and the computation result.
--
-- USE THIS FUNCTION WITH UNSAFE SYNC FFI CALL ONLY.
withPrimUnsafe :: (Prim a) => a -> (MBA# a -> IO b) -> IO (a, b)
withPrimUnsafe v f = do
  -- All heap objects are WORD aligned so no need to do extra alignment
  mpa@(MutablePrimArray mba#) <- newPrimArray 1
  writePrimArray mpa 0 v
  !b <- f (MBA# mba#)
  !a <- readPrimArray mpa 0
  return (a, b)
{-# INLINE withPrimUnsafe #-}

-- From Z-Data package: Z.Foreign
--
-- | like 'withPrimUnsafe', but don't write initial value.
--
-- USE THIS FUNCTION WITH UNSAFE SYNC FFI CALL ONLY.
allocPrimUnsafe :: (Prim a) => (MBA# a -> IO b) -> IO (a, b)
allocPrimUnsafe f = do
  -- All heap objects are WORD aligned so no need to do extra alignment
  mpa@(MutablePrimArray mba#) <- newPrimArray 1
  !b <- f (MBA# mba#)
  !a <- readPrimArray mpa 0
  return (a, b)
{-# INLINE allocPrimUnsafe #-}

-- From Z-Data package: Z.Foreign
--
-- | Pass primitive array to safe FFI as pointer.
--
-- Use proper pointer type and @HsInt@ to marshall @Ptr a@ and @Int@ arguments
-- on C side.
-- The memory pointed by 'Ptr a' will not moved during call. After call returned,
-- pointer is no longer valid.
--
-- The second 'Int' arguement is the element size not the bytes size.
--
-- Don't pass a forever loop to this function,
-- see <https://ghc.haskell.org/trac/ghc/ticket/14346 #14346>.
withPrimArray :: (Prim a) => PrimArray a -> (Ptr a -> Int -> IO b) -> IO b
withPrimArray arr f
  | isPrimArrayPinned arr = do
      let siz = sizeofPrimArray arr
      withPrimArrayContents arr $ \ptr -> f ptr siz
  | otherwise = do
      let siz = sizeofPrimArray arr
      buf <- newPinnedPrimArray siz
      copyPrimArray buf 0 arr 0 siz
      withMutablePrimArrayContents buf $ \ptr -> f ptr siz
{-# INLINABLE withPrimArray #-}

-- From Z-Data package: Z.Foreign
--
-- | Allocate a prim array and pass to FFI as pointer, freeze result into a 'PrimVector'.
allocPrimArray :: forall a b . Prim a
               => Int      -- ^ in elements
               -> (Ptr a -> IO b)
               -> IO (PrimArray a, b)
allocPrimArray len f = do
  mpa <- newAlignedPinnedPrimArray len
  !r <- withMutablePrimArrayContents mpa f
  !pa <- unsafeFreezePrimArray mpa
  return (pa, r)
{-# INLINABLE allocPrimArray #-}

-- From Z-Data package: Z.Foreign
--
-- | Pass primitive array to unsafe FFI as pointer.
--
-- Enable 'UnliftedFFITypes' extension in your haskell code, use proper pointer
-- type and @HsInt@ to marshall @ByteArray#@ and @Int@ arguments on C side.
--
-- The second 'Int' arguement is the element size not the bytes size.
--
-- USE THIS FUNCTION WITH UNSAFE SYNC FFI CALL ONLY.
withPrimArrayUnsafe :: (Prim a) => PrimArray a -> (BA# a -> Int -> IO b) -> IO b
withPrimArrayUnsafe pa@(PrimArray ba#) f = f (BA# ba#) (sizeofPrimArray pa)
{-# INLINE withPrimArrayUnsafe #-}

-- From Z-Data package: Z.Foreign
--
-- | Allocate some bytes and pass to FFI as pointer, freeze result into a
-- 'PrimArray'.
--
-- USE THIS FUNCTION WITH UNSAFE SYNC FFI CALL ONLY.
allocPrimArrayUnsafe
  :: forall a b. Prim a => Int -> (MBA# a -> IO b) -> IO (PrimArray a, b)
allocPrimArrayUnsafe len f = do
  (mpa@(MutablePrimArray mba#) :: MutablePrimArray RealWorld a) <- newPrimArray len
  !r <- f (MBA# mba#)
  !pa <- unsafeFreezePrimArray mpa
  return (pa, r)
{-# INLINE allocPrimArrayUnsafe #-}

-- From Z-Data package: Z.Foreign
--
-- | Pass primitive array list to safe FFI as pointer.
--
-- Use proper pointer type and @HsInt@ to marshall @Ptr (Ptr a)@ and @Int@
-- arguments on C side.
-- The memory pointed by 'Ptr a' will not moved during call. After call returned,
-- pointer is no longer valid.
--
-- The second 'Int' arguement is the list size.
--
-- Don't pass a forever loop to this function,
-- see <https://ghc.haskell.org/trac/ghc/ticket/14346 #14346>.
withPrimArrayList
  :: Prim a => [PrimArray a] -> (Ptr (Ptr a) -> Int -> IO b) -> IO b
withPrimArrayList pas0 f = do
  let l = length pas0
  ptrs <- newPinnedPrimArray l
  go ptrs 0 pas0
  where
    go ptrs !_ [] = do pa <- unsafeFreezePrimArray ptrs
                       withPrimArray pa f
    go ptrs !i (pa:pas) =
      -- It's important to nest 'withPrimArray' calls to keep all pointers alive
      withPrimArray pa $ \ ppa _ -> do
        writePrimArray ptrs i ppa
        go ptrs (i+1) pas

withForeignPtrList :: [ForeignPtr a] -> (Ptr (Ptr a) -> Int -> IO b) -> IO b
withForeignPtrList fptrs f = do
  let l = length fptrs
  ptrs <- newPinnedPrimArray l
  go ptrs 0 fptrs
  where
    go ptrs !_ [] = do
      pa <- unsafeFreezePrimArray ptrs
      withPrimArray pa f
    go ptrs !i (fp:fps) = do
      withForeignPtr fp $ \p -> do
        writePrimArray ptrs i p
        go ptrs (i+1) fps

-- From Z-Data package: Z.Foreign, with slight modification.
--
-- | Pass primitive array list to unsafe FFI as @StgArrBytes**@.
--
-- Enable 'UnliftedFFITypes' extension in your haskell code, use
-- @StgArrBytes**@(>=8.10) or @StgMutArrPtrs*@(<8.10) pointer type and @HsInt@
-- to marshall @BAArray#@ and @Int@ arguments on C side.
--
-- The second 'Int' arguement is the list size.
--
-- USE THIS FUNCTION WITH UNSAFE FFI CALL ONLY.
withPrimArrayListUnsafe :: [PrimArray a] -> (BAArray# a -> Int -> IO b) -> IO b
withPrimArrayListUnsafe pas f = do
  let l = length pas
  mla <- unsafeNewUnliftedArray l
  foldM_ (\ !i pa -> writeUnliftedArray mla i pa >> return (i + 1)) 0 pas
  (UnliftedArray la#) <- unsafeFreezeUnliftedArray mla
  f (BAArray# (unsafeCoerce# la#)) l
{-# INLINE withPrimArrayListUnsafe #-}

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

#if __GLASGOW_HASKELL__ < 902
-- ghc<9.2 does not has a 'mutableByteArrayContents#'
mutableByteArrayContents# :: MutableByteArray# s -> Addr#
mutableByteArrayContents# mba# = byteArrayContents# (unsafeCoerce# mba#)
{-# INLINE mutableByteArrayContents# #-}
#endif

-- From Z-Data package
--
-- | Obtain the pointer to the content of an mutable array, and the pointer
-- should only be used during the IO action.
--
-- This operation is only safe on /pinned/ primitive arrays (Arrays allocated
-- by 'newPinnedPrimArray' or 'newAlignedPinnedPrimArray').
--
-- Don't pass a forever loop to this function,
-- see <https://ghc.haskell.org/trac/ghc/ticket/14346 #14346>.
withMutablePrimArrayContents
  :: MutablePrimArray RealWorld a
  -> (Ptr a -> IO b)
  -> IO b
withMutablePrimArrayContents (MutablePrimArray mba#) f = do
    let addr# = mutableByteArrayContents# mba#
        ptr = Ptr addr#
    b <- f ptr
    primitive_ (touch# mba#)
    return b
{-# INLINE withMutablePrimArrayContents #-}

-- From Z-Data package
--
-- | Obtain the pointer to the content of an array, and the pointer should only
-- be used during the IO action.
--
-- This operation is only safe on /pinned/ primitive arrays (Arrays allocated
-- by 'newPinnedPrimArray' or 'newAlignedPinnedPrimArray').
--
-- Don't pass a forever loop to this function,
-- see <https://ghc.haskell.org/trac/ghc/ticket/14346 #14346>.
withPrimArrayContents :: PrimArray a -> (Ptr a -> IO b) -> IO b
withPrimArrayContents (PrimArray ba#) f = do
    let addr# = byteArrayContents# ba#
        ptr = Ptr addr#
    b <- f ptr
    primitive_ (touch# ba#)
    return b
{-# INLINE withPrimArrayContents #-}