packages feed

trexio-hs-0.1.1: src/TREXIO/Internal/Marshaller.hs

module TREXIO.Internal.Marshaller where

import Control.Exception
import Data.Massiv.Array as Massiv hiding (withMArray)
import Data.Massiv.Array.Unsafe
import Data.Maybe (fromJust)
import Foreign

{- | Passes a mutable 'MArray' to a C function. The array may be deallocated
on C side or modified in place. This funciton is super dangerous. You array may
suddenly be gone!
-}
withMArray :: (Index ix) => MArray s S ix a -> (Ptr b -> IO c) -> IO c
withMArray v f = do
  let (arrFPtr, _arrL) = unsafeMArrayToForeignPtr v
  withForeignPtr arrFPtr $ \arrPtr -> f (castPtr arrPtr)

{- | Pass a 'Massiv.Array' to a C function. This function is safe, but the array
is copied to a new memory location.
-}
withArray :: (Storable a, Index ix) => Array S ix a -> (Ptr b -> IO c) -> IO c
withArray v f = do
  mArr <- thaw v
  withMArray mArr f

{- | Get an 'MArray' from C memory. Haskell and C side use the same memory reference.
Be careful with this function. When the undelying pointer vanishes, the array is nonsense
-}
unsafeToMArray :: (Index ix, Storable a) => Sz ix -> Ptr a -> IO (MArray s S ix a)
unsafeToMArray sz ptr = do
  fPtr <- newForeignPtr_ ptr
  let mArr = unsafeMArrayFromForeignPtr0 fPtr (toLinearSz sz)
  resizeMArrayM sz mArr

{- | Get an 'Array' from C memory. The underlying C array is copied and the
result is safe to use.
-}
peekArray :: (Index ix, Storable a) => Sz ix -> Ptr a -> IO (Array S ix a)
peekArray sz ptr = do
  mArr <- unsafeToMArray sz ptr
  freeze Par mArr

-- | Peek an integer array from C memory and cast it into standard int types.
peekIntArray :: (Index ix, Manifest r Int) => Sz ix -> Ptr Int32 -> IO (Array r ix Int)
peekIntArray sz ptr = do
  mArr <- unsafeToMArray sz (castPtr ptr)
  compute . Massiv.map (fromIntegral :: Int32 -> Int) <$> freeze Par mArr

-- | Peek 2D coordinates from C memory.
peek2DCoords :: (Manifest r Ix2) => Sz1 -> Ptr Int32 -> IO (Vector r Ix2)
peek2DCoords sz ptr = do
  coordsSimple :: Matrix S Int <- peekIntArray (consSz sz 2) ptr
  return . compute . Massiv.map mkIx2 . outerSlices $ coordsSimple
 where
  mkIx2 v = Ix2 (v ! 0) (v ! 1)

-- | Peek 3D coordinates from C memory.
peek3DCoords :: (Manifest r Ix3) => Sz1 -> Ptr Int32 -> IO (Vector r Ix3)
peek3DCoords sz ptr = do
  coordsSimple :: Matrix S Int <- peekIntArray (consSz sz 3) ptr
  return . compute . Massiv.map mkIx3 . outerSlices $ coordsSimple
 where
  mkIx3 v = Ix3 (v ! 0) (v ! 1) (v ! 2)

-- | Peek 4D coordinates from C memory.
peek4DCoords :: (Manifest r Ix4) => Sz1 -> Ptr Int32 -> IO (Vector r Ix4)
peek4DCoords sz ptr = do
  coordsSimple :: Matrix S Int <- peekIntArray (consSz sz 4) ptr
  return . compute . Massiv.map mkIx4 . outerSlices $ coordsSimple
 where
  mkIx4 v = Ix4 (v ! 0) (v ! 1) (v ! 2) (v ! 3)

-- | Peek 5D coordinates from C memory.
peek6DCoords :: (Manifest r (IxN 6)) => Sz1 -> Ptr Int32 -> IO (Vector r (IxN 6))
peek6DCoords sz ptr = do
  coordsSimple :: Matrix S Int <- peekIntArray (consSz sz 6) ptr
  return . compute . Massiv.map mkIx6 . outerSlices $ coordsSimple
 where
  mkIx6 v = (v ! 0) :> (v ! 1) :> (v ! 2) :> (v ! 3) :> (v ! 4) :. (v ! 5)

peek8DCoords :: (Manifest r (IxN 8)) => Sz1 -> Ptr Int32 -> IO (Vector r (IxN 8))
peek8DCoords sz ptr = do
  coordsSimple :: Matrix S Int <- peekIntArray (consSz sz 8) ptr
  return . compute . Massiv.map mkIx8 . outerSlices $ coordsSimple
 where
  mkIx8 v = (v ! 0) :> (v ! 1) :> (v ! 2) :> (v ! 3) :> (v ! 4) :> (v ! 5) :> (v ! 6) :. (v ! 7)

-- | Convert a vector of 'Ix2' to a \( m \times 2 \) matrix.
castCoords2D :: (Manifest r1 Ix2, Manifest r2 Int32) => Vector r1 Ix2 -> Matrix r2 Int32
castCoords2D ixVec =
  compute
    . fromJust
    . stackOuterSlicesM
    . Massiv.map ix2ToRow
    $ ixVec
 where
  ix2ToRow :: Ix2 -> Vector S Int32
  ix2ToRow (Ix2 x y) = fromList Seq [fromIntegral x, fromIntegral y]

-- | Convert a vector of 'Ix3' to a \( m \times 3 \) matrix.
castCoords3D :: (Manifest r1 Ix3, Manifest r2 Int32) => Vector r1 Ix3 -> Matrix r2 Int32
castCoords3D ixVec =
  compute
    . fromJust
    . stackOuterSlicesM
    . Massiv.map ix3ToRow
    $ ixVec
 where
  ix3ToRow :: Ix3 -> Vector S Int32
  ix3ToRow (Ix3 x y z) =
    fromList
      Seq
      [ fromIntegral x
      , fromIntegral y
      , fromIntegral z
      ]

-- | Convert a vector of 'Ix4' to a \( m \times 4 \) matrix.
castCoords4D :: (Manifest r1 Ix4, Manifest r2 Int32) => Vector r1 Ix4 -> Matrix r2 Int32
castCoords4D ixVec =
  compute
    . fromJust
    . stackOuterSlicesM
    . Massiv.map ix4ToRow
    $ ixVec
 where
  ix4ToRow :: Ix4 -> Vector S Int32
  ix4ToRow (Ix4 x y z w) =
    fromList
      Seq
      [ fromIntegral x
      , fromIntegral y
      , fromIntegral z
      , fromIntegral w
      ]

-- | Convert a vector of 'Ix6' to a \( m \times 6 \) matrix.
castCoords6D :: (Manifest r1 (IxN 6), Manifest r2 Int32) => Vector r1 (IxN 6) -> Matrix r2 Int32
castCoords6D ixVec =
  compute
    . fromJust
    . stackOuterSlicesM
    . Massiv.map ix6ToRow
    $ ixVec
 where
  ix6ToRow :: IxN 6 -> Vector S Int32
  ix6ToRow (x :> y :> z :> w :> u :. v) =
    fromList
      Seq
      [ fromIntegral x
      , fromIntegral y
      , fromIntegral z
      , fromIntegral w
      , fromIntegral u
      , fromIntegral v
      ]

castCoords8D :: (Manifest r1 (IxN 8), Manifest r2 Int32) => Vector r1 (IxN 8) -> Matrix r2 Int32
castCoords8D ixVec =
  compute
    . fromJust
    . stackOuterSlicesM
    . Massiv.map ix8ToRow
    $ ixVec
 where
  ix8ToRow :: IxN 8 -> Vector S Int32
  ix8ToRow (x :> y :> z :> w :> u :> v :> t :. s) =
    fromList
      Seq
      [ fromIntegral x
      , fromIntegral y
      , fromIntegral z
      , fromIntegral w
      , fromIntegral u
      , fromIntegral v
      , fromIntegral t
      , fromIntegral s
      ]

-- | Like 'callocArray' but safe with bracket pattern
callocaArray :: (Storable a) => Int -> (Ptr a -> IO b) -> IO b
callocaArray nEl =
  bracket
    (callocArray nEl)
    free