packed-data-0.2.0.0: src/Data/Packed/Reader.hs
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE UnboxedTuples #-}
-- |
-- It is recommended to import this module like so:
--
-- @
-- import qualified Data.Packed.Reader as R
-- @
module Data.Packed.Reader (
PackedReader (..),
mkPackedReader,
runReader,
-- * Applicative
pure,
liftA,
liftA2,
(<*>),
(*>),
(<*),
-- * Monad
(>>=),
(>>),
fail,
return,
lift,
-- * Application
with,
threadedWith,
-- * Fragments
PackedFragment (..),
castPackedFragment,
) where
import Control.Monad.Identity
import Data.Bifunctor
import Data.ByteString.Internal
import Data.Kind
import Data.Packed.Internal
import Data.Packed.Packed
import Data.Packed.Utils ((:++:))
import Foreign hiding (with)
import Prelude hiding (fail, liftA2, pure, return, (*>), (<*), (<*>), (>>), (>>=))
import qualified Prelude
-- | Basically a function that reads/desrialises a value from a 'Data.Packed.Packed'
--
-- 'p' the types of the packed values to read
--
-- 'r' the packed type after the encoded values to read
--
-- 'v' the type of the value to unpack
--
-- __Note:__ It is an indexed monad.
newtype PackedReader p r v = PackedReader
{ runReaderStep :: PackedFragment (p :++: r) -> Identity (v, PackedFragment r)
}
-- | Represents position of the cursor in the packed buffer to traverse
data PackedFragment (p :: [Type])
= PF
-- | Pointer in the packed buffer
{-# UNPACK #-} !(Ptr Word8)
-- | Number of bytes between the cursor's position and the end of the buffer
{-# UNPACK #-} !Int
{-# INLINE castPackedFragment #-}
castPackedFragment :: PackedFragment p -> PackedFragment t
castPackedFragment (PF p t) = PF p t
{-# INLINE mkPackedReader #-}
-- | Builds a 'Data.Packed.Reader.PackedReader'
mkPackedReader ::
(PackedFragment (p :++: r) -> Identity (v, PackedFragment r)) ->
PackedReader p r v
mkPackedReader = PackedReader
instance Functor (PackedReader p r) where
{-# INLINE fmap #-}
fmap f (PackedReader reader) = PackedReader $ fmap (first f) . reader
-------- Applicative
-- | Similar to 'Control.Applicative.pure'
{-# INLINE pure #-}
pure :: v -> PackedReader '[] r v
pure = return
-- | Similar to 'Control.Applicative.liftA'
{-# INLINE liftA #-}
liftA :: (a -> b) -> PackedReader p t a -> PackedReader p t b
liftA = fmap
-- | Similar to 'Control.Applicative.liftA2'
{-# INLINE liftA2 #-}
liftA2 ::
(v1 -> v2 -> v3) ->
PackedReader p (r1 :++: r2) v1 ->
PackedReader r1 r2 v2 ->
PackedReader (p :++: r1) r2 v3
liftA2 f m1 m2 = PackedReader $ \pf -> do
(!v1, !pf1) <- runReaderStep m1 (castPackedFragment pf)
(!v2, !pf2) <- runReaderStep m2 pf1
Prelude.return (f v1 v2, pf2)
-- | Similar to 'Control.Applicative.<*>'
{-# INLINE (<*>) #-}
(<*>) :: PackedReader p (r1 :++: r2) (v1 -> v2) -> PackedReader r1 r2 v1 -> PackedReader (p :++: r1) r2 v2
(<*>) = liftA2 id
-- | Similar to 'Control.Applicative.*>'
{-# INLINE (*>) #-}
(*>) :: PackedReader p (r1 :++: r2) v -> PackedReader r1 r2 v' -> PackedReader (p :++: r1) r2 v'
(*>) = (>>)
-- | Similar to 'Control.Applicative.<*'
{-# INLINE (<*) #-}
(<*) :: PackedReader p (r1 :++: r2) v2 -> PackedReader r1 r2 v1 -> PackedReader (p :++: r1) r2 v2
(<*) = liftA2 const
-------- Monad
-- | Allows bindings 'Data.Packed.Reader.PackedReader' together, in a monad-like manner.
--
-- Similar to 'Prelude.>>='
{-# INLINE (>>=) #-}
(>>=) ::
PackedReader p (r1 :++: r2) v ->
(v -> PackedReader r1 r2 v') ->
PackedReader (p :++: r1) r2 v'
(>>=) pr1 next = PackedReader $ \pf ->
let Identity (!v1, !pf1) = runReaderStep pr1 (castPackedFragment pf)
PackedReader !pr2 = next v1
in pr2 pf1
{-# INLINE (>>) #-}
-- | Similar to 'Prelude.>>'
(>>) ::
PackedReader p (r1 :++: r2) v ->
PackedReader r1 r2 v' ->
PackedReader (p :++: r1) r2 v'
(>>) pr1 (PackedReader pr2) = PackedReader $ \pf ->
let
Identity (!_, !pf1) = runReaderStep pr1 (castPackedFragment pf)
in
pr2 pf1
{-# INLINE return #-}
-- | Like 'Prelude.return', wraps a value in a 'Data.Packed.Reader.PackedReader' that will not consume its input.
return :: v -> PackedReader '[] r v
return !value = PackedReader $ \(!pf) -> Identity (value, pf)
-- | Interrupts a 'Data.Packed.Reader.PackedReader' computation
{-# INLINE fail #-}
fail :: String -> PackedReader '[] r v
fail msg = mkPackedReader $ \_ -> error msg
-- | Run the reading function using a 'Data.Packed.Packed'.
{-# INLINE runReader #-}
runReader ::
PackedReader p r v ->
Packed (p :++: r) ->
(v, Packed r)
runReader pr (Packed (BS fptr l)) =
unsafeDupablePerformIO
( withForeignPtr fptr $ \ptr -> do
let Identity (!v, !(PF ptr1 l1)) = runReaderStep pr (PF (castPtr ptr) l)
!fptr1 <- newForeignPtr_ ptr1
Prelude.return (v, Packed (BS fptr1 l1))
)
-- | Consumes the input 'Data.Packed.Reader.PackedFragment'.
--
-- To be used inside a 'Data.Packed.Reader.PackedReader' computation.
--
-- Example:
--
-- @
-- sumPacked :: PackedReader '[Tree Int] r Int
-- sumPacked = PackedReader $ \case
-- PackedLeaf l -> reader `with` l
-- PackedNode n -> threadedWith n $ R.do
-- !left <- sumPacked2
-- !right <- sumPacked2
-- let !res = left + right
-- R.return res
-- @
{-# INLINE with #-}
with :: PackedReader p r v -> PackedFragment (p :++: r) -> Identity (v, PackedFragment r)
with = runReaderStep
-- | Flipped version of 'with'
{-# INLINE threadedWith #-}
threadedWith :: PackedFragment (p :++: r) -> PackedReader p r v -> Identity (v, PackedFragment r)
threadedWith = flip with
-- | Allows running a 'Data.Packed.Reader.PackedReader' computation inside an other
{-# INLINE lift #-}
lift ::
PackedReader a b v ->
PackedFragment (a :++: b) ->
PackedReader '[] r v
lift r p = mkPackedReader $ \pf ->
let
Identity !(!res, !_) = runReaderStep r p
in
Identity (res, pf)