module Data.Packer.Internal
( Packing(..)
, Hole
, Unpacking(..)
, Memory(..)
, OutOfBoundUnpacking(..)
, OutOfBoundPacking(..)
, HoleInPacking(..)
, IsolationNotFullyConsumed(..)
, unpackUnsafeActRef
, unpackCheckActRef
, unpackUnsafeAct
, unpackCheckAct
, unpackIsolate
, unpackLookahead
, unpackSetPosition
, unpackGetPosition
, unpackGetNbRemaining
, packCheckAct
, packHole
, packGetPosition
, fillHole
) where
import Foreign.Ptr
import Foreign.ForeignPtr
import Data.Data
import Data.Word
import Control.Exception (Exception, throwIO, try, SomeException)
import Control.Monad.IO.Class
import Control.Applicative (Alternative(..), Applicative(..), (<$>), (<*>))
import Control.Concurrent.MVar
import Control.Monad (when)
data Memory = Memory !(Ptr Word8)
!Int
newtype Packing a = Packing { runPacking_ :: (Ptr Word8, MVar Int) -> Memory -> IO (a, Memory) }
instance Monad Packing where
return = returnPacking
(>>=) = bindPacking
instance MonadIO Packing where
liftIO f = Packing $ \_ st -> f >>= \a -> return (a,st)
instance Functor Packing where
fmap = fmapPacking
instance Applicative Packing where
pure = returnPacking
(<*>) = apPacking
bindPacking :: Packing a -> (a -> Packing b) -> Packing b
bindPacking m1 m2 = Packing $ \cst st -> do
(a, st2) <- runPacking_ m1 cst st
runPacking_ (m2 a) cst st2
fmapPacking :: (a -> b) -> Packing a -> Packing b
fmapPacking f m = Packing $ \cst st -> runPacking_ m cst st >>= \(a, st2) -> return (f a, st2)
returnPacking :: a -> Packing a
returnPacking a = Packing $ \_ st -> return (a,st)
apPacking :: Packing (a -> b) -> Packing a -> Packing b
apPacking fm m = fm >>= \p -> m >>= \r2 -> return (p r2)
newtype Unpacking a = Unpacking { runUnpacking_ :: (ForeignPtr Word8, Memory) -> Memory -> IO (a, Memory) }
instance Monad Unpacking where
return = returnUnpacking
(>>=) = bindUnpacking
instance MonadIO Unpacking where
liftIO f = Unpacking $ \_ st -> f >>= \a -> return (a,st)
instance Functor Unpacking where
fmap = fmapUnpacking
instance Applicative Unpacking where
pure = returnUnpacking
(<*>) = apUnpacking
instance Alternative Unpacking where
empty = error "Data.Packer (Alternative): empty"
f <|> g = Unpacking $ \cst st ->
tryRunUnpacking f cst st >>= either (const $ runUnpacking_ g cst st) return
tryRunUnpacking :: Unpacking a -> (ForeignPtr Word8, Memory) -> Memory -> IO (Either SomeException (a,Memory))
tryRunUnpacking f cst st = try $ runUnpacking_ f cst st
bindUnpacking :: Unpacking a -> (a -> Unpacking b) -> Unpacking b
bindUnpacking m1 m2 = Unpacking $ \cst st -> do
(a, st2) <- runUnpacking_ m1 cst st
runUnpacking_ (m2 a) cst st2
fmapUnpacking :: (a -> b) -> Unpacking a -> Unpacking b
fmapUnpacking f m = Unpacking $ \cst st -> runUnpacking_ m cst st >>= \(a, st2) -> return (f a, st2)
returnUnpacking :: a -> Unpacking a
returnUnpacking a = Unpacking $ \_ st -> return (a,st)
apUnpacking :: Unpacking (a -> b) -> Unpacking a -> Unpacking b
apUnpacking fm m = fm >>= \p -> m >>= \r2 -> return (p r2)
data OutOfBoundPacking = OutOfBoundPacking Int
Int
deriving (Show,Eq,Data,Typeable)
data HoleInPacking = HoleInPacking Int
deriving (Show,Eq,Data,Typeable)
data OutOfBoundUnpacking = OutOfBoundUnpacking Int
Int
deriving (Show,Eq,Data,Typeable)
data IsolationNotFullyConsumed = IsolationNotFullyConsumed Int
Int
deriving (Show,Eq,Data,Typeable)
instance Exception OutOfBoundPacking
instance Exception HoleInPacking
instance Exception OutOfBoundUnpacking
instance Exception IsolationNotFullyConsumed
unpackUnsafeActRef :: Int
-> (ForeignPtr Word8 -> Ptr Word8 -> IO a)
-> Unpacking a
unpackUnsafeActRef n act = Unpacking $ \(fptr, iniBlock) st@(Memory ptr sz) -> do
r <- act fptr ptr
return (r, Memory (ptr `plusPtr` n) (sz n))
unpackCheckActRef :: Int
-> (ForeignPtr Word8 -> Ptr Word8 -> IO a)
-> Unpacking a
unpackCheckActRef n act = Unpacking $ \(fptr, iniBlock@(Memory iniPtr _)) (Memory ptr sz) -> do
when (sz < n) (throwIO $ OutOfBoundUnpacking (ptr `minusPtr` iniPtr) n)
r <- act fptr ptr
return (r, Memory (ptr `plusPtr` n) (sz n))
unpackIsolate :: Int
-> Unpacking a
-> Unpacking a
unpackIsolate n sub = Unpacking $ \(fptr, iniBlock@(Memory iniPtr _)) (Memory ptr sz) -> do
when (sz < n) (throwIO $ OutOfBoundUnpacking (ptr `minusPtr` iniPtr) n)
(r, Memory newPtr subLeft) <- (runUnpacking_ sub) (fptr,iniBlock) (Memory ptr n)
when (subLeft > 0) $ (throwIO $ IsolationNotFullyConsumed n subLeft)
return (r, Memory newPtr (sz n))
unpackUnsafeAct :: Int -> (Ptr Word8 -> IO a) -> Unpacking a
unpackUnsafeAct n act = unpackUnsafeActRef n (\_ -> act)
unpackCheckAct :: Int -> (Ptr Word8 -> IO a) -> Unpacking a
unpackCheckAct n act = unpackCheckActRef n (\_ -> act)
unpackSetPosition :: Int -> Unpacking ()
unpackSetPosition pos = Unpacking $ \(fptr, iniBlock@(Memory iniPtr sz)) _ -> do
when (pos < 0 || pos > sz) (throwIO $ OutOfBoundUnpacking pos 0)
return ((), Memory (iniPtr `plusPtr` pos) (szpos))
unpackGetPosition :: Unpacking Int
unpackGetPosition = Unpacking $
\(_, (Memory iniPtr _)) st@(Memory ptr _) -> return (ptr `minusPtr` iniPtr, st)
unpackGetNbRemaining :: Unpacking Int
unpackGetNbRemaining = Unpacking $ \_ st@(Memory _ sz) -> return (sz,st)
unpackLookahead :: (Ptr Word8 -> Int -> IO a)
-> Unpacking a
unpackLookahead f = Unpacking $
\_ st@(Memory ptr sz) -> f ptr sz >>= \a -> return (a, st)
packCheckAct :: Int -> (Ptr Word8 -> IO a) -> Packing a
packCheckAct n act = Packing $ \_ (Memory ptr sz) -> do
when (sz < n) (throwIO $ OutOfBoundPacking sz n)
r <- act ptr
return (r, Memory (ptr `plusPtr` n) (sz n))
modifyHoles :: (Int -> Int) -> Packing ()
modifyHoles f = Packing $ \(_, holesMVar) mem -> modifyMVar_ holesMVar (\v -> return $! f v) >> return ((), mem)
packGetPosition :: Packing Int
packGetPosition = Packing $ \(iniPtr, _) mem@(Memory ptr _) -> return (ptr `minusPtr` iniPtr, mem)
newtype Hole a = Hole (a -> IO ())
packHole :: Int -> (Ptr Word8 -> a -> IO ()) -> Packing (Hole a)
packHole n f = do
r <- packCheckAct n (\ptr -> return $ Hole (\w -> f ptr w))
modifyHoles (1 +)
return r
fillHole :: Hole a -> a -> Packing ()
fillHole (Hole closure) a = modifyHoles (\i -> i 1) >> liftIO (closure a)