packages feed

primal-memory-0.3.0.0: src/Data/Prim/Memory/PArray.hs

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RoleAnnotations #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
-- |
-- Module      : Data.Prim.Memory.PArray
-- Copyright   : (c) Alexey Kuleshevich 2020
-- License     : BSD3
-- Maintainer  : Alexey Kuleshevich <alexey@kuleshevi.ch>
-- Stability   : experimental
-- Portability : non-portable
--
module Data.Prim.Memory.PArray
  ( PArray(..)
  , PMArray(..)
  , Pinned(..)
  , fromBytesPArray
  , toBytesPArray
  , fromUArrayPArray
  , toUArrayPArray
  , castPArray
  , fromMBytesPMArray
  , toMBytesPMArray
  , fromUMArrayPMArray
  , toUMArrayPMArray
  , castPMArray
  , allocPMArray
  , allocPinnedPMArray
  , allocAlignedPMArray
  , allocUnpinnedPMArray
  , shrinkPMArray
  , resizePMArray
  , reallocPMArray
  , isPinnedPArray
  , isPinnedPMArray

  , thawPArray
  , freezePMArray
  , sizePArray
  , getSizePMArray
  , readPMArray
  , writePMArray

  , setPMArray
  , copyPArrayToPMArray
  , movePMArrayToPMArray
  ) where

import Control.DeepSeq
import Control.Prim.Monad
import Data.Prim
import Data.Prim.Array (Size(..), UArray(..), UMArray(..))
import Data.Prim.Memory.Bytes
import Data.Prim.Memory.Fold
import Data.Prim.Memory.ForeignPtr
import Data.Prim.Memory.Internal
import Foreign.Prim

-- | An immutable array with elements of type @e@
newtype PArray (p :: Pinned) e = PArray (Bytes p)
  deriving (NFData, Semigroup, Monoid, MemRead)
type role PArray nominal nominal


instance (Prim e, Eq e) => Eq (PArray p e) where
  (==) = eqMem @e
  {-# INLINE (==) #-}

instance (Prim e, Ord e) => Ord (PArray p e) where
  compare = compareMem @e
  {-# INLINE compare #-}

-- | A mutable array with elements of type @e@
newtype PMArray (p :: Pinned) e s = PMArray (MBytes p s)
  deriving (NFData, MemWrite)
type role PMArray nominal nominal nominal

-- | Read-only access, but it is not enforced.
instance PtrAccess s (PArray 'Pin e) where
  toForeignPtr = pure . toForeignPtrBytes . toBytesPArray
  {-# INLINE toForeignPtr #-}
  withPtrAccess b = withPtrBytes (toBytesPArray b)
  {-# INLINE withPtrAccess #-}
  withNoHaltPtrAccess b = withNoHaltPtrBytes (toBytesPArray b)
  {-# INLINE withNoHaltPtrAccess #-}

instance PtrAccess s (PMArray 'Pin e s) where
  toForeignPtr = pure . toForeignPtrMBytes . toMBytesPMArray
  {-# INLINE toForeignPtr #-}
  withPtrAccess mb = withPtrMBytes (toMBytesPMArray mb)
  {-# INLINE withPtrAccess #-}
  withNoHaltPtrAccess mb = withNoHaltPtrMBytes (toMBytesPMArray mb)
  {-# INLINE withNoHaltPtrAccess #-}

instance Typeable p => MemAlloc (PMArray p e) where
  type FrozenMem (PMArray p e) = PArray p e
  getByteCountMutMem = getByteCountMutMem . toMBytesPMArray
  {-# INLINE getByteCountMutMem #-}
  allocMutMem = fmap fromMBytesPMArray . allocMBytes
  {-# INLINE allocMutMem #-}
  thawMem = thawPArray
  {-# INLINE thawMem #-}
  freezeMutMem = freezePMArray
  {-# INLINE freezeMutMem #-}
  reallocMutMem mba = fmap fromMBytesPMArray . reallocMBytes (toMBytesPMArray mba)
  {-# INLINE reallocMutMem #-}

instance (Typeable p, Prim e) => IsList (PArray p e) where
  type Item (PArray p e) = e
  fromList = fromListMem
  fromListN n = fromListZeroMemN_ (Count n)
  toList = toListMem

instance Typeable p => IsString (PArray p Char) where
  fromString = fromListMem

instance (Show e, Prim e) => Show (PArray p e) where
  show = show . toListPArray


toListPArray :: Prim e => PArray p e -> [e]
toListPArray = toListMem

castPArray :: PArray p e' -> PArray p e
castPArray = coerce

-- | /O(1)/ - Cast `PArray` to `UArray`
--
-- @since 0.3.0
toUArrayPArray :: PArray p e -> UArray e
toUArrayPArray pa = UArray (toByteArray# (toBytesPArray pa))
{-# INLINE toUArrayPArray #-}

-- | /O(1)/ - Cast `UArray` to `PArray`
--
-- @since 0.3.0
fromUArrayPArray :: UArray e -> PArray 'Inc e
fromUArrayPArray (UArray ba#) = fromBytesPArray (fromByteArray# ba#)
{-# INLINE fromUArrayPArray #-}

fromBytesPArray :: Bytes p -> PArray p e
fromBytesPArray = coerce

toBytesPArray :: PArray p e -> Bytes p
toBytesPArray = coerce

castPMArray :: PMArray p e' s -> PMArray p e s
castPMArray = coerce

fromMBytesPMArray :: MBytes p s -> PMArray p e s
fromMBytesPMArray = coerce

toMBytesPMArray :: PMArray p e s -> MBytes p s
toMBytesPMArray = coerce

-- | /O(1)/ - Cast `PMArray` to `UMArray`
--
-- @since 0.3.0
toUMArrayPMArray :: PMArray p e s -> UMArray e s
toUMArrayPMArray pa = UMArray (toMutableByteArray# (toMBytesPMArray pa))
{-# INLINE toUMArrayPMArray #-}

-- | /O(1)/ - Cast `UMArray` to `PMArray`
--
-- @since 0.3.0
fromUMArrayPMArray :: UMArray e s -> PMArray 'Inc e s
fromUMArrayPMArray (UMArray mba#) = fromMBytesPMArray (fromMutableByteArray# mba#)
{-# INLINE fromUMArrayPMArray #-}


sizePArray :: forall e p. Prim e => PArray p e -> Size
sizePArray = (coerce :: Count e -> Size) . countBytes . toBytesPArray
{-# INLINE sizePArray #-}

getSizePMArray :: forall e p m s. (MonadPrim s m, Prim e) => PMArray p e s -> m Size
getSizePMArray = fmap (coerce :: Count e -> Size) . getCountMBytes . toMBytesPMArray
{-# INLINE getSizePMArray #-}

allocPMArray ::
     forall e p m s . (Typeable p, Prim e, MonadPrim s m) => Size -> m (PMArray p e s)
allocPMArray sz = fromMBytesPMArray <$> allocMBytes (coerce sz :: Count e)
{-# INLINE allocPMArray #-}

allocUnpinnedPMArray :: forall e m s . (MonadPrim s m, Prim e) => Size -> m (PMArray 'Inc e s)
allocUnpinnedPMArray sz = fromMBytesPMArray <$> allocUnpinnedMBytes (coerce sz :: Count e)
{-# INLINE allocUnpinnedPMArray #-}

allocPinnedPMArray :: forall e m s . (MonadPrim s m, Prim e) => Size -> m (PMArray 'Pin e s)
allocPinnedPMArray sz = fromMBytesPMArray <$> allocPinnedMBytes (coerce sz :: Count e)
{-# INLINE allocPinnedPMArray #-}

allocAlignedPMArray ::
     (MonadPrim s m, Prim e)
  => Count e -- ^ Size in number of bytes
  -> m (PMArray 'Pin e s)
allocAlignedPMArray = fmap fromMBytesPMArray . allocAlignedMBytes
{-# INLINE allocAlignedPMArray #-}

freezePMArray :: MonadPrim s m => PMArray p e s -> m (PArray p e)
freezePMArray = fmap fromBytesPArray . freezeMBytes . toMBytesPMArray
{-# INLINE freezePMArray #-}

thawPArray :: MonadPrim s m => PArray p e -> m (PMArray p e s)
thawPArray = fmap fromMBytesPMArray . thawBytes . toBytesPArray
{-# INLINE thawPArray #-}

-- | Shrink mutable bytes to new specified count of elements. The new count must be less
-- than or equal to the current count as reported by `getCountPMArray`.
shrinkPMArray ::
     forall e p m s. (MonadPrim s m, Prim e)
  => PMArray p e s
  -> Size
  -> m ()
shrinkPMArray mba sz = shrinkMBytes (toMBytesPMArray mba) (coerce sz :: Count e)
{-# INLINE shrinkPMArray #-}


-- | Attempt to resize mutable bytes in place.
--
-- * New bytes might be allocated, with the copy of an old one.
-- * Old references should not be kept around to allow GC to claim it
-- * Old references should not be used to avoid undefined behavior
resizePMArray ::
     forall e p m s. (MonadPrim s m, Prim e)
  => PMArray p e s
  -> Size
  -> m (PMArray 'Inc e s)
resizePMArray mba sz =
  fromMBytesPMArray <$>
  resizeMBytes (toMBytesPMArray mba) (coerce sz :: Count e)
{-# INLINE resizePMArray #-}

reallocPMArray ::
     forall e p m s. (MonadPrim s m, Typeable p,  Prim e)
  => PMArray p e s
  -> Size
  -> m (PMArray p e s)
reallocPMArray mba sz =
  fromMBytesPMArray <$>
  reallocMBytes (toMBytesPMArray mba) (coerce sz :: Count e)
{-# INLINABLE reallocPMArray #-}


isPinnedPArray :: PArray p e -> Bool
isPinnedPArray (PArray b) = isPinnedBytes b
{-# INLINE isPinnedPArray #-}

isPinnedPMArray :: PMArray p e s -> Bool
isPinnedPMArray (PMArray mb) = isPinnedMBytes mb
{-# INLINE isPinnedPMArray #-}

readPMArray :: (MonadPrim s m, Prim e) => PMArray p e s -> Int -> m e
readPMArray (PMArray mb) = readOffMBytes mb . coerce
{-# INLINE readPMArray #-}

writePMArray :: (MonadPrim s m, Prim e) => PMArray p e s -> Int -> e -> m ()
writePMArray (PMArray mb) o = writeOffMBytes mb (coerce o)
{-# INLINE writePMArray #-}



setPMArray ::
     forall e p m s. (MonadPrim s m, Prim e)
  => PMArray p e s -- ^ Chunk of memory to fill
  -> Int -- ^ Offset in number of elements
  -> Size -- ^ Number of cells to fill
  -> e -- ^ A value to fill the cells with
  -> m ()
setPMArray (PMArray mb) off sz = setMBytes mb (coerce off) (coerce sz)
{-# INLINE setPMArray #-}

copyPArrayToPMArray ::
     forall e p m s. (MonadPrim s m, Prim e)
  => PArray p e
  -> Int
  -> PMArray p e s
  -> Int
  -> Size
  -> m ()
copyPArrayToPMArray ba srcOff mba dstOff sz =
  copyMem ba (coerce srcOff) mba (coerce dstOff) (coerce sz `countForProxyTypeOf` ba)
{-# INLINE copyPArrayToPMArray #-}

movePMArrayToPMArray ::
     forall e p m s. (MonadPrim s m, Prim e)
  => PMArray p e s
  -> Int
  -> PMArray p e s
  -> Int
  -> Size
  -> m ()
movePMArrayToPMArray ba srcOff mba dstOff sz =
  moveMutMem ba (coerce srcOff) mba (coerce dstOff) (coerce sz :: Count e)
{-# INLINE movePMArrayToPMArray #-}



-- toPtrPArray :: PArray Pin e -> Ptr e
-- toPtrPArray (PArray ba#) = Ptr (byteArrayContents# ba#)
-- {-# INLINE toPtrPArray #-}

-- toPtrPMArray :: PMArray Pin e s -> Ptr e
-- toPtrPMArray (PMArray mba#) = Ptr (mutablePArrayContents# mba#)
-- {-# INLINE toPtrPMArray #-}

-- -- | Pointer access to immutable `PArray` should be for read only purposes, but it is
-- -- not enforced. Any mutation will break referential transparency
-- withPtrPArray :: MonadPrim s m => PArray Pin e -> (Ptr e -> m b) -> m b
-- withPtrPArray b f = do
--   res <- f (toPtrPArray b)
--   res <$ touch b
-- {-# INLINE withPtrPArray #-}

-- -- | Same as `withPtrPArray`, but is suitable for actions that don't terminate
-- withNoHaltPtrPArray :: MonadUnliftPrim s m => PArray Pin e -> (Ptr e -> m b) -> m b
-- withNoHaltPtrPArray b f = withAliveUnliftPrim b $ f (toPtrPArray b)
-- {-# INLINE withNoHaltPtrPArray #-}

-- withPtrPMArray :: MonadPrim s m => PMArray Pin e s -> (Ptr e -> m b) -> m b
-- withPtrPMArray mb f = do
--   res <- f (toPtrPMArray mb)
--   res <$ touch mb
-- {-# INLINE withPtrPMArray #-}

-- withNoHaltPtrPMArray :: MonadUnliftPrim s m => PMArray Pin e s -> (Ptr e -> m b) -> m b
-- withNoHaltPtrPMArray mb f = withAliveUnliftPrim mb $ f (toPtrPMArray mb)
-- {-# INLINE withNoHaltPtrPMArray #-}


-- -- -- | Check if two byte arrays refer to pinned memory and compare their pointers.
-- -- isSamePArray :: PArray p1 e -> PArray p2 e -> Bool
-- -- isSamePArray (PArray b1#) (PArray b2#) = isTrue# (isSameByteArray# b1# b2#)
-- -- {-# INLINE[0] isSamePArray #-}
-- -- {-# RULES
-- -- "isSamePinnedPArray" isSamePArray = isSamePinnedPArray
-- --   #-}

-- -- -- | Perform pointer equality on pinned `PArray`.
-- -- isSamePinnedPArray :: PArray Pin e -> PArray Pin e -> Bool
-- -- isSamePinnedPArray pb e1 pb2 = toPtrPArray pb e1 == toPtrPArray pb e2
-- -- {-# INLINE isSamePinnedPArray #-}



-- -- byteStringConvertError :: String -> a
-- -- byteStringConvertError msg = error $ "Cannot convert 'ByteString'. " ++ msg
-- -- {-# NOINLINE byteStringConvertError #-}