-- | Working with 'Foreign.Ptr's in a way that prevents use after free -- -- >>> :set -XPostfixOperators -- >>> import Control.Monad.Scoped.Internal -- >>> scoped do x <- newPtr (69 :: Word); x `setPtr` 42; getPtr x -- 42 -- -- @since 0.1.0.0 module Control.Monad.Scoped.Ptr ( Ptr , foreignPtr , wrapScoped , newPtr , setPtr , getPtr ) where import Control.Monad.IO.Unlift (MonadIO (liftIO), MonadUnliftIO (withRunInIO)) import Control.Monad.Scoped.Internal (Scoped (UnsafeMkScoped), ScopedResource (UnsafeMkScopedResource, unsafeUnwrapScopedResource), bracketScoped, (:<)) import Control.Monad.Trans.Class (lift) import Foreign qualified -- | A 'Foreign.Ptr' that is associated to a scope -- -- @since 0.1.0.0 type Ptr s a = ScopedResource s (Foreign.Ptr a) -- | Acquire mutable memory for the duration of a scope. The value is automatically dropped at the end of the scope. -- -- @since 0.2.0.0 newPtr :: (Foreign.Storable a, MonadUnliftIO m) => a -> Scoped (s : ss) m (Ptr s a) newPtr a = bracketScoped (liftIO $ Foreign.new a) (liftIO . Foreign.free) -- | write a value to a pointer -- -- @since 0.2.0.0 setPtr :: (Foreign.Storable a, MonadIO m, s :< ss) => Ptr s a -> a -> Scoped ss m () setPtr ptr = liftIO . Foreign.poke (unsafeUnwrapScopedResource ptr) -- | read a value from a pointer -- -- @since 0.2.0.0 getPtr :: (Foreign.Storable a, MonadIO m, s :< ss) => Ptr s a -> Scoped ss m a getPtr ptr = liftIO (Foreign.peek (unsafeUnwrapScopedResource ptr)) -- | this is a wrapper around 'Foreign.withForeignPtr' to allow for safe usage of this function in a scope -- -- @since 0.1.0.2 foreignPtr :: MonadUnliftIO m => Foreign.ForeignPtr a -> Scoped (s : ss) m (Ptr s a) foreignPtr fptr = UnsafeMkScoped \k -> withRunInIO \inIO -> Foreign.withForeignPtr fptr (inIO . k . UnsafeMkScopedResource) -- | takes a function that does something with a 'Foreign.Ptr' and makes it safe -- -- @since 0.1.0.2 wrapScoped :: (Monad m, s :< ss) => (Foreign.Ptr a -> m r) -> Ptr s a -> Scoped ss m r wrapScoped k p = lift $ k (unsafeUnwrapScopedResource p)