{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}

-- | Utilities for FFI
module Cardano.Foreign (
  -- * Sized pointer
  SizedPtr (..),
  allocaSized,
  memcpySized,
  memsetSized,

  -- * Low-level C functions
  c_memcpy,
  c_memset,
) where

import Control.Monad (void)
import Data.Proxy (Proxy (..))
import Data.Void (Void)
import Data.Word (Word8)
import Foreign.C.Types (CSize (..))
import Foreign.Marshal.Alloc (allocaBytes)
import Foreign.Ptr (Ptr)
import GHC.TypeLits

-------------------------------------------------------------------------------
-- Sized pointer
-------------------------------------------------------------------------------

-- A pointer which knows the size of underlying memory block
newtype SizedPtr (n :: Nat) = SizedPtr (Ptr Void)

-- | Like 'allocaBytes'.
allocaSized :: forall n b. KnownNat n => (SizedPtr n -> IO b) -> IO b
allocaSized :: forall (n :: Nat) b. KnownNat n => (SizedPtr n -> IO b) -> IO b
allocaSized SizedPtr n -> IO b
k = forall a b. Int -> (Ptr a -> IO b) -> IO b
allocaBytes Int
size (SizedPtr n -> IO b
k forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (n :: Nat). Ptr Void -> SizedPtr n
SizedPtr)
  where
    size :: Int
    size :: Int
size = forall a. Num a => Integer -> a
fromInteger (forall (n :: Nat) (proxy :: Nat -> *).
KnownNat n =>
proxy n -> Integer
natVal (forall {k} (t :: k). Proxy t
Proxy @n))

memcpySized :: forall n. KnownNat n => SizedPtr n -> SizedPtr n -> IO ()
memcpySized :: forall (n :: Nat). KnownNat n => SizedPtr n -> SizedPtr n -> IO ()
memcpySized (SizedPtr Ptr Void
dest) (SizedPtr Ptr Void
src) = forall (f :: * -> *) a. Functor f => f a -> f ()
void (forall a. Ptr a -> Ptr a -> CSize -> IO (Ptr ())
c_memcpy Ptr Void
dest Ptr Void
src CSize
size)
  where
    size :: CSize
    size :: CSize
size = forall a. Num a => Integer -> a
fromInteger (forall (n :: Nat) (proxy :: Nat -> *).
KnownNat n =>
proxy n -> Integer
natVal (forall {k} (t :: k). Proxy t
Proxy @n))

memsetSized :: forall n. KnownNat n => SizedPtr n -> Word8 -> IO ()
memsetSized :: forall (n :: Nat). KnownNat n => SizedPtr n -> Word8 -> IO ()
memsetSized (SizedPtr Ptr Void
s) Word8
c = forall (f :: * -> *) a. Functor f => f a -> f ()
void (forall a. Ptr a -> Int -> CSize -> IO (Ptr ())
c_memset Ptr Void
s (forall a b. (Integral a, Num b) => a -> b
fromIntegral Word8
c) CSize
size)
  where
    size :: CSize
    size :: CSize
size = forall a. Num a => Integer -> a
fromInteger (forall (n :: Nat) (proxy :: Nat -> *).
KnownNat n =>
proxy n -> Integer
natVal (forall {k} (t :: k). Proxy t
Proxy @n))

-------------------------------------------------------------------------------
-- Some C functions
-------------------------------------------------------------------------------

-- | @void *memcpy(void *dest, const void *src, size_t n);@
--
-- Note: this is safe foreign import
foreign import ccall "memcpy"
  c_memcpy :: Ptr a -> Ptr a -> CSize -> IO (Ptr ())

-- | @void *memset(void *s, int c, size_t n);@
--
-- Note: for sure zeroing memory use @c_sodium_memzero@.
foreign import ccall "memset"
  c_memset :: Ptr a -> Int -> CSize -> IO (Ptr ())