{-# LANGUAGE UnicodeSyntax, NoImplicitPrelude #-}

-------------------------------------------------------------------------------
-- |
-- Module      :  Foreign.Ptr.Region
-- Copyright   :  (c) 2010 Bas van Dijk
-- License     :  BSD3 (see the file LICENSE)
-- Maintainer  :  Bas van Dijk <v.dijk.bas@gmail.com>
--
-------------------------------------------------------------------------------

module Foreign.Ptr.Region
    ( -- * Regional pointers
      RegionalPtr

      {-| Note that this module re-exports the @Control.Monad.Trans.Region@
      module from the @regions@ package which allows you to:

      * Run a region using 'runRegionT'.

      * Concurrently run a region inside another region using 'forkTopRegion'.

      * Duplicate a 'RegionalPtr' to a parent region using 'dup'.
      -}
    , module Control.Monad.Trans.Region

      -- * Constructing regional pointers
    , regionalPtr
    , nullPtr

      -- *  Pure functions on regional pointers
    , mapRegionalPtr

    , castPtr
    , plusPtr
    , alignPtr
    , minusPtr
    ) where


--------------------------------------------------------------------------------
-- Imports
--------------------------------------------------------------------------------

-- from base:
import Control.Monad                     ( return, (>>=), fail )
import Data.Function                     ( ($) )
import Data.Int                          ( Int )
import Data.Maybe                        ( Maybe(Nothing, Just) )
import           Foreign.Ptr             ( Ptr )
import qualified Foreign.Ptr as FP       ( nullPtr
                                         , castPtr, plusPtr, alignPtr, minusPtr
                                         )

-- from transformers:
import Control.Monad.IO.Class            ( MonadIO )

-- from regions:
import Control.Monad.Trans.Region     -- (re-exported entirely)
import Control.Monad.Trans.Region.OnExit ( CloseAction, onExit )

-- from ourselves:
import Foreign.Ptr.Region.Internal       ( RegionalPtr(RegionalPtr) )
import Foreign.Ptr.Region.Unsafe         ( unsafePtr )


--------------------------------------------------------------------------------
-- * Constructing regional pointers
--------------------------------------------------------------------------------

-- | Construct a regional pointer from a native pointer and an @IO@ computation
-- that frees the pointer which is executed when the region exits.
regionalPtr  MonadIO pr
             Ptr α
             CloseAction
             RegionT s pr (RegionalPtr α (RegionT s pr))
regionalPtr ptr freePtr = do ch  onExit freePtr
                             return $ RegionalPtr ptr $ Just ch

-- | The constant @nullPtr@ contains a distinguished value of 'RegionalPtr'
-- that is not associated with a valid memory location.
--
-- Note that @nullPtr@ is a pure value. This means it does not perform the
-- side-effect of registering a 'CloseAction' like @free nullPtr@ in the
-- 'RegionT' monad.
nullPtr  RegionalPtr α r
nullPtr = RegionalPtr FP.nullPtr Nothing


--------------------------------------------------------------------------------
-- * Pure functions on regional pointers
--------------------------------------------------------------------------------

-- | Apply a /pure/ function to the inner pointer of a regional pointer.
mapRegionalPtr  (Ptr α  Ptr β)  (RegionalPtr α r  RegionalPtr β r)
mapRegionalPtr f = \(RegionalPtr ptr ch)  RegionalPtr (f ptr) ch

-- | The @castPtr@ function casts a pointer from one type to another.
--
-- Wraps: @Foreign.Ptr.@'FP.castPtr'
castPtr  RegionalPtr α r  RegionalPtr β r
castPtr = mapRegionalPtr FP.castPtr

-- | Advances the given address by the given offset in bytes.
--
-- Wraps: @Foreign.Ptr.@'FP.plusPtr'
plusPtr  RegionalPtr α r  Int  RegionalPtr β r
plusPtr rp n = mapRegionalPtr (\p  FP.plusPtr p n) rp

-- | Given an arbitrary address and an alignment constraint, @alignPtr@ yields
-- the next higher address that fulfills the alignment constraint. An alignment
-- constraint @x@ is fulfilled by any address divisible by @x@.  This operation
-- is idempotent.
--
-- Wraps: @Foreign.Ptr.@'FP.alignPtr'
alignPtr  RegionalPtr α r  Int  RegionalPtr α r
alignPtr rp n = mapRegionalPtr (\p  FP.alignPtr p n) rp

-- | Computes the offset required to get from the second to the first
-- argument. We have
--
-- > p2 == p1 `plusPtr` (p2 `minusPtr` p1)
--
-- Wraps: @Foreign.Ptr.@'FP.minusPtr'
minusPtr  RegionalPtr α r1  RegionalPtr β r2  Int
minusPtr rp1 rp2 = FP.minusPtr (unsafePtr rp1) (unsafePtr rp2)


-- The End ---------------------------------------------------------------------