{-# LANGUAGE CPP, MagicHash, UnboxedTuples #-}
{-|
This module assumes that you are familiar with the funcitons
of the same name in Foreign.Marshall.Alloc, Foreign.Marshal.AllocArray
and in Foreign.ForeignPtr.
The functions are generalized to work in any monad which is an
instance of MonadCatchIO.
-}
module Control.Monad.CatchIO.Foreign
( alloca
, allocaBytes
, allocaArray
, allocaArray0
, withForeignPtr
)
where
-- this is a bit dodgy - we use the MIN_VERSION macro to
-- see if we're compiled against transformers or mtl
#ifdef MIN_VERSION_transformers
import Control.Monad.IO.Class(liftIO)
#else
import Control.Monad.Trans (liftIO)
#endif
import Control.Monad.CatchIO
import qualified Foreign as F
import Foreign (Ptr,ForeignPtr, sizeOf, alignment, Storable)
#ifdef __GLASGOW_HASKELL__
import qualified Data.Primitive as P
import GHC.Exts
import GHC.IOBase hiding (liftIO)
#endif
alloca :: (F.Storable a, MonadCatchIO m) => (Ptr a -> m b) -> m b
alloca = doAlloca undefined
where
doAlloca :: (MonadCatchIO m', Storable a') => a' -> (Ptr a' -> m' b') -> m' b'
doAlloca dummy = allocaBytesAligned (F.sizeOf dummy) (F.alignment dummy)
allocaBytes :: (MonadCatchIO m) => Int -> (Ptr a -> m b) -> m b
allocaBytes size = bracket (liftIO $ F.mallocBytes size) (liftIO . F.free)
allocaBytesAligned :: (MonadCatchIO m) => Int -> Int -> (Ptr a -> m b) -> m b
#ifdef __GLASGOW_HASKELL__
allocaBytesAligned size alignment k
= do
ba <- liftIO $ P.newAlignedPinnedByteArray size alignment >>= P.unsafeFreezeByteArray
r <- k $ case P.byteArrayContents ba of
P.Addr addr# -> Ptr addr#
liftIO $ touch ba
return r
touch :: a -> IO ()
touch a = IO $ \s -> case touch# a s of
s' -> (# s', () #)
#else
allocaBytesAligned size _ = allocaBytes size -- wrong, but the FFI doesn't offer anything else
#endif
allocaArray :: (F.Storable a, MonadCatchIO m) => Int -> (Ptr a -> m b) -> m b
allocaArray = doAlloca undefined
where
doAlloca :: (Storable a', MonadCatchIO m') => a' -> Int -> (Ptr a' -> m' b') -> m' b'
doAlloca dummy size = allocaBytesAligned (size * sizeOf dummy) (alignment dummy)
allocaArray0 :: (F.Storable a, MonadCatchIO m) => Int -> (Ptr a -> m b) -> m b
allocaArray0 size = allocaArray (size + 1)
withForeignPtr :: (MonadCatchIO m) => ForeignPtr a -> (Ptr a -> m b) -> m b
withForeignPtr fo io
= do r <- io (F.unsafeForeignPtrToPtr fo)
liftIO $ F.touchForeignPtr fo
return r