h-raylib-5.1.2.0: src/Raylib/Internal/Foreign.hs
{-# OPTIONS -Wall #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleInstances #-}
-- |
-- Miscellaneous utility functions for marshalling values to/from C. The most
-- notable thing in this module is the `Freeable` typeclass.
module Raylib.Internal.Foreign
( c'free,
p'free,
freeMaybePtr,
Freeable (..),
rlFreeMaybeArray,
pop,
popCArray,
popCString,
withFreeable,
withFreeableArray,
withFreeableArrayLen,
withFreeableArray2D,
configsToBitflag,
withMaybe,
withMaybeCString,
withCStringBuffer,
peekMaybe,
peekMaybeOff,
pokeMaybe,
pokeMaybeOff,
peekMaybeArray,
newMaybeArray,
peekStaticArray,
peekStaticArrayOff,
pokeStaticArray,
pokeStaticArrayOff,
rightPad,
)
where
import Control.Monad (forM_, unless)
import Data.Bits ((.|.))
import qualified Data.ByteString as BS
import Data.Maybe (fromMaybe)
import qualified Data.Text as T
import qualified Data.Text.Encoding as TE
import Foreign (FunPtr, Ptr, Storable (peek, peekByteOff, poke, sizeOf), allocaBytes, castPtr, malloc, newArray, nullPtr, peekArray, plusPtr, pokeArray0, with, withArray, withArrayLen)
import Foreign.C (CFloat, CInt, CString, CUChar, CUInt, peekCString, withCString)
import Foreign.C.Types (CBool, CChar, CShort, CUShort)
-- Internal utility functions
#ifdef WEB_FFI
import Raylib.Internal.Web.Native (jsfree, p'jsfree)
c'free :: Ptr () -> IO ()
c'free = jsfree
p'free :: FunPtr (Ptr a -> IO ())
p'free = p'jsfree
#else
foreign import ccall "stdlib.h free" c'free :: Ptr () -> IO ()
foreign import ccall "stdlib.h &free" p'free :: FunPtr (Ptr a -> IO ())
#endif
freeMaybePtr :: Ptr () -> IO ()
freeMaybePtr ptr = unless (ptr == nullPtr) (c'free ptr)
-- | A typeclass used internally to free complex data types. You will most
-- likely not have to use this directly. If you do need to implement it, you
-- can probably just stick with the default definitions of `rlFree` and
-- `rlFreeDependents`.
class Freeable a where
-- | Frees the data \"dependent\" on a pointer, which usually means dynamic
-- C arrays, i.e. more pointers
rlFreeDependents :: a -> Ptr a -> IO ()
rlFreeDependents _ _ = return ()
-- | Receives a pointer and frees all of the data associated with it,
-- including the pointer itself
rlFree :: a -> Ptr a -> IO ()
rlFree val ptr = rlFreeDependents val ptr >> c'free (castPtr ptr)
instance Freeable CBool
instance Freeable CChar
instance Freeable CFloat
instance Freeable CInt
instance Freeable CShort
instance Freeable CUChar
instance Freeable CUInt
instance Freeable CUShort
instance Freeable (Ptr CChar) where
rlFreeDependents str _ = c'free (castPtr str)
instance (Freeable a, Storable a) => Freeable [a] where
rlFreeDependents arr ptr =
forM_
[0 .. length arr - 1]
( \i -> do
let val = arr !! i in rlFreeDependents val (plusPtr ptr (i * sizeOf val))
)
rlFreeMaybeArray :: (Freeable a, Storable a) => Maybe [a] -> Ptr a -> IO ()
rlFreeMaybeArray Nothing _ = return ()
rlFreeMaybeArray (Just arr) ptr = rlFree arr (castPtr ptr)
pop :: (Freeable a, Storable a) => Ptr a -> IO a
pop ptr = do
val <- peek ptr
rlFree val ptr
return val
popCArray :: (Freeable a, Storable a) => Int -> Ptr a -> IO [a]
popCArray count ptr = do
str <- peekArray count ptr
c'free $ castPtr ptr
return str
popCString :: CString -> IO String
popCString ptr = do
str <- peekCString ptr
c'free $ castPtr ptr
return str
withFreeable :: (Freeable a, Storable a) => a -> (Ptr a -> IO b) -> IO b
withFreeable val f = do
with
val
( \ptr -> do
result <- f ptr
rlFreeDependents val ptr
return result
)
withFreeableArray :: (Freeable a, Storable a) => [a] -> (Ptr a -> IO b) -> IO b
withFreeableArray arr f = do
withArray
arr
( \ptr -> do
result <- f ptr
rlFreeDependents arr (castPtr ptr)
return result
)
withFreeableArrayLen :: (Freeable a, Storable a) => [a] -> (Int -> Ptr a -> IO b) -> IO b
withFreeableArrayLen arr f = do
withArrayLen
arr
( \l ptr -> do
result <- f l ptr
rlFreeDependents arr (castPtr ptr)
return result
)
withFreeableArray2D :: (Freeable a, Storable a) => [[a]] -> (Ptr (Ptr a) -> IO b) -> IO b
withFreeableArray2D arr func = do
arrays <- mapM newArray arr
ptr <- newArray arrays
res <- func ptr
forM_ (zip [0 ..] arrays) (\(i, a) -> rlFree (arr !! i) (castPtr a))
c'free $ castPtr ptr
return res
configsToBitflag :: (Enum a) => [a] -> Integer
configsToBitflag = fromIntegral . foldr folder (toEnum 0)
where
folder a b = fromEnum a .|. b
withMaybe :: (Storable a) => Maybe a -> (Ptr a -> IO b) -> IO b
withMaybe a f = case a of
(Just val) -> with val f
Nothing -> f nullPtr
withMaybeCString :: Maybe String -> (CString -> IO b) -> IO b
withMaybeCString a f = case a of
(Just val) -> withCString val f
Nothing -> f nullPtr
withCStringBuffer :: String -> Maybe Int -> (Int -> CString -> IO b) -> IO (b, String)
withCStringBuffer str bufferSize f = do
let bytes = BS.unpack (TE.encodeUtf8 (T.pack str))
bufferSize' = fromMaybe (length bytes + 8) bufferSize
allocaBytes
bufferSize'
( \buffer -> do
pokeArray0 0 buffer (map fromIntegral bytes)
res <- f bufferSize' buffer
str' <- peekCString buffer
return (res, str')
)
peekMaybe :: (Storable a) => Ptr (Ptr a) -> IO (Maybe a)
peekMaybe ptr = do
ref <- peek ptr
if ref == nullPtr then return Nothing else Just <$> peek ref
peekMaybeOff :: (Storable a) => Ptr (Ptr a) -> Int -> IO (Maybe a)
peekMaybeOff ptr off = do
ref <- peekByteOff ptr off
if ref == nullPtr then return Nothing else Just <$> peek ref
pokeMaybe :: (Storable a) => Ptr (Ptr a) -> Maybe a -> IO ()
pokeMaybe ptr val = case val of
Nothing -> poke ptr nullPtr
Just a -> do
nested <- malloc
poke nested a
poke ptr nested
pokeMaybeOff :: (Storable a) => Ptr (Ptr a) -> Int -> Maybe a -> IO ()
pokeMaybeOff ptr off = pokeMaybe (plusPtr ptr off)
peekMaybeArray :: (Storable a) => Int -> Ptr a -> IO (Maybe [a])
peekMaybeArray size ptr = if ptr == nullPtr then return Nothing else Just <$> peekArray size ptr
newMaybeArray :: (Storable a) => Maybe [a] -> IO (Ptr a)
newMaybeArray a = case a of
(Just arr) -> newArray arr
Nothing -> return nullPtr
peekStaticArray :: (Storable a) => Int -> Ptr a -> IO [a]
peekStaticArray size ptr = reverse <$> helper size ptr []
where
helper s p a =
if s == 0
then return a
else do
val <- peek p
helper (s - 1) (plusPtr p (sizeOf val)) (val : a)
peekStaticArrayOff :: (Storable a) => Int -> Ptr a -> Int -> IO [a]
peekStaticArrayOff size ptr off = peekStaticArray size (plusPtr ptr off)
pokeStaticArray :: (Storable a) => Ptr a -> [a] -> IO ()
pokeStaticArray _ [] = return ()
pokeStaticArray ptr (x : xs) = poke ptr x >> pokeStaticArray (plusPtr ptr $ sizeOf x) xs
pokeStaticArrayOff :: (Storable a) => Ptr a -> Int -> [a] -> IO ()
pokeStaticArrayOff ptr off = pokeStaticArray (plusPtr ptr off)
rightPad :: Int -> a -> [a] -> [a]
rightPad size val arr = take size $ arr ++ repeat val