{-# LANGUAGE CPP #-}

-- | Read-only pointers
--
-- This module is intended to be imported qualified.
--
-- > import HsBindgen.Runtime.Prelude
-- > import HsBindgne.Runtime.PtrConst qualified as PtrConst
module HsBindgen.Runtime.PtrConst (
    PtrConst -- type synonym or opaque, depending on version of @base@
  , peek
  , unsafeToPtr
  , unsafeFromPtr
    -- * Relationship with 'ConstPtr'
    -- $constptr
  ) where

#if MIN_VERSION_base(4,18,0)
import Foreign.C.ConstPtr (ConstPtr(..))
#endif

import Data.Kind (Type)
import Foreign.Ptr (Ptr)
import Foreign.Storable (Storable)
import Foreign.Storable qualified as F

-- | A read-only pointer.
--
-- A @'PtrConst' a@ is a pointer to a type @a@ with a C @const@ qualifier. For
-- instance, the Haskell type @PtrConst CInt@ is equivalent to the C type @const
-- int*@. @const@-qualified contents of a pointer should not be modified, but
-- reading the contents is okay.
type PtrConst :: Type -> Type
#if MIN_VERSION_base(4,18,0)
type PtrConst a = ConstPtr a
#else
type role PtrConst phantom
newtype PtrConst a = Wrap { unwrap :: Ptr a }
    deriving stock (Eq, Ord)
    deriving newtype Storable

-- doesn't use record syntax
instance Show (PtrConst a) where
    showsPrec d (Wrap p) = showParen (d > 10) $ showString "PtrConst " . showsPrec 11 p
#endif

{-------------------------------------------------------------------------------
  Internal
-------------------------------------------------------------------------------}

unPtrConst :: PtrConst a -> Ptr a
#if MIN_VERSION_base(4,18,0)
unPtrConst :: forall a. PtrConst a -> Ptr a
unPtrConst = ConstPtr a -> Ptr a
forall a. PtrConst a -> Ptr a
unConstPtr
#else
unPtrConst = unwrap
#endif

mkPtrConst :: Ptr a -> PtrConst a
#if MIN_VERSION_base(4,18,0)
mkPtrConst :: forall a. Ptr a -> PtrConst a
mkPtrConst = Ptr a -> ConstPtr a
forall a. Ptr a -> PtrConst a
ConstPtr
#else
mkPtrConst = Wrap
#endif

{-------------------------------------------------------------------------------
  Public
-------------------------------------------------------------------------------}

-- | Like 'F.peek'
peek :: Storable a => PtrConst a -> IO a
peek :: forall a. Storable a => PtrConst a -> IO a
peek PtrConst a
ptrc = Ptr a -> IO a
forall a. Storable a => Ptr a -> IO a
F.peek (PtrConst a -> Ptr a
forall a. PtrConst a -> Ptr a
unPtrConst PtrConst a
ptrc)

-- | Unsafe: convert a 'PtrConst' to a 'Ptr'.
--
-- NOTE: use the output pointer only with read access.
unsafeToPtr :: PtrConst a -> Ptr a
unsafeToPtr :: forall a. PtrConst a -> Ptr a
unsafeToPtr PtrConst a
ptrc = PtrConst a -> Ptr a
forall a. PtrConst a -> Ptr a
unPtrConst PtrConst a
ptrc

-- | Unsafe: convert a 'Ptr' to a 'PtrConst'.
--
-- NOTE: use the input pointer only with read access.
unsafeFromPtr :: Ptr a -> PtrConst a
unsafeFromPtr :: forall a. Ptr a -> PtrConst a
unsafeFromPtr Ptr a
ptr = Ptr a -> PtrConst a
forall a. Ptr a -> PtrConst a
mkPtrConst Ptr a
ptr

{-------------------------------------------------------------------------------
  Relationship with 'ConstPtr'
-------------------------------------------------------------------------------}

{- $constptr

'PtrConst' is a pointer-to-const-data, but 'ConstPtr' is too. They are mostly
equivalent, so why a new type? For two main reasons:

1. 'ConstPtr' is actually a misnomer: it is a pointer-to-const-data, not a
   const-pointer-to-data.
2. 'ConstPtr' can be freely converted to a 'Ptr', even though its contents
   should be considered to be read-only.

'PtrConst' is arguably a better name, and it goes further in ensuring that
the pointer only has read access.

On @base-4.18@ and up, 'PtrConst' is a type synonym around 'ConstPtr', while on
earlier @base@ versions it is a custom, opaque datatype. If you care about
compatibility with all @base@ versions that @hs-bindgen-runtime@ support, then
you should treat 'PtrConst' as its own type distinct from 'ConstPtr' using only
the functions in the "HsBindgen.Runtime.PtrConst" module rather than the
"Foreign.C.ConstPtr" module.
-}