packages feed

sdl3-bindgen-sys-0.0.0.1: runtime/HsBindgen/Runtime/Support/SizedByteArray.hs

{-# OPTIONS_HADDOCK hide #-}

module HsBindgen.Runtime.Support.SizedByteArray (
  SizedByteArray (..),
  zeroUnionValue,
) where

import Data.Coerce (Coercible, coerce)
import Data.Primitive.ByteArray (ByteArray (..))
import Data.Primitive.ByteArray qualified as BA
import Data.Proxy (Proxy (..))
import Data.Word (Word8)
import Foreign (Storable (..))
import Foreign.Ptr (Ptr, castPtr)
import GHC.TypeNats qualified as GHC

import HsBindgen.Runtime.Marshal

{-------------------------------------------------------------------------------
  Definition
-------------------------------------------------------------------------------}

-- | t'SizedByteArray's provide deriving-via support for t'ByteArray'.
--
-- Intended usage:
--
-- > newtype Foo = Foo ByteArray
-- >   deriving (Storable, Prim) via SizedByteArray 16 4
--
-- == Size
--
-- In this example, the v'ByteArray' must have size 16.
--
-- == Storable
--
-- The derived 'Storable' instance does /not/ declare that the t'ByteArray'
-- /itself/ is memory aligned in any way (indeed, the t'ByteArray' may well not
-- be pinned). It merely states that if we use 'Storable' to pass the
-- t'ByteArray' to a C function, we must
--
-- * allocate a temporary buffer (typically using 'Foreign.alloca')
-- * copy the v'ByteArray' into that buffer
-- * call the C function, passing a pointer to this buffer
--
-- where /that temporary buffer/ must be memory aligned.
newtype SizedByteArray (size :: GHC.Nat) (alignment :: GHC.Nat)
  = SizedByteArray ByteArray
  deriving (Storable) via EquivStorable (SizedByteArray size alignment)

{-------------------------------------------------------------------------------
  StaticSize, ReadRaw, WriteRaw
-------------------------------------------------------------------------------}

instance (GHC.KnownNat n, GHC.KnownNat m) => StaticSize (SizedByteArray n m) where
  staticSizeOf _ = fromIntegral (GHC.natVal (Proxy @n))
  staticAlignment _ = fromIntegral (GHC.natVal (Proxy @m))

instance (GHC.KnownNat n) => ReadRaw (SizedByteArray n m) where
  readRaw ptrSBA = do
    let ptr = castPtr ptrSBA :: Ptr Word8
        size = fromIntegral $ GHC.natVal (Proxy @n)
    arr <- BA.newByteArray size
    BA.copyPtrToMutableByteArray arr 0 ptr size
    SizedByteArray <$> BA.unsafeFreezeByteArray arr

-- | Write a t'SizedByteArray' to the specified location, which must have the
-- correct alignment (matching @m@)
instance (GHC.KnownNat n) => WriteRaw (SizedByteArray n m) where
  writeRaw ptrSBA (SizedByteArray arr) = do
    let ptr = castPtr ptrSBA :: Ptr Word8
        size = fromIntegral $ GHC.natVal (Proxy @n)
    BA.copyByteArrayToAddr ptr arr 0 size

{-# DEPRECATED zeroUnionValue "Use HsBindgen.Runtime.Union.zero instead" #-}

-- | Create a value of a C union with all bytes initialized to zero.
zeroUnionValue :: forall a. (Coercible a ByteArray, StaticSize a) => a
zeroUnionValue = coerce $ BA.byteArrayFromListN n $ replicate n (0 :: Word8)
 where
  n = staticSizeOf (Proxy :: Proxy a)