module Data.Repa.Convert.Internal.Packer
        ( Packer (..)
        , unsafeRunPacker)
where
import Data.Word
import Data.IORef
import GHC.Exts
import qualified Foreign.Ptr    as F


-- | Packer wraps a function that can write to a buffer.
data Packer
  =  Packer
  { -- | Takes start of buffer; failure action; and a continuation.
    --
    --   We try to pack data into the given buffer.
    --   If packing succeeds then we call the continuation with a pointer
    --   to the next byte after the packed value,
    --   otherwise we call the failure action.
    --
    Packer -> Addr# -> IO () -> (Addr# -> IO ()) -> IO ()
fromPacker
        :: Addr#                -- Start of buffer.
        -> IO ()                -- Signal failure.
        -> (Addr# -> IO ())     -- Accept the address after the packed value.
        -> IO ()
  }

instance Semigroup Packer where
 <> :: Packer -> Packer -> Packer
(<>) = Packer -> Packer -> Packer
forall a. Monoid a => a -> a -> a
mappend


instance Monoid Packer where
 mempty :: Packer
mempty
  = (Addr# -> IO () -> (Addr# -> IO ()) -> IO ()) -> Packer
Packer ((Addr# -> IO () -> (Addr# -> IO ()) -> IO ()) -> Packer)
-> (Addr# -> IO () -> (Addr# -> IO ()) -> IO ()) -> Packer
forall a b. (a -> b) -> a -> b
$ \Addr#
buf IO ()
_fail Addr# -> IO ()
k -> Addr# -> IO ()
k Addr#
buf
 {-# INLINE mempty #-}

 mappend :: Packer -> Packer -> Packer
mappend (Packer Addr# -> IO () -> (Addr# -> IO ()) -> IO ()
fa) (Packer Addr# -> IO () -> (Addr# -> IO ()) -> IO ()
fb)
  = (Addr# -> IO () -> (Addr# -> IO ()) -> IO ()) -> Packer
Packer ((Addr# -> IO () -> (Addr# -> IO ()) -> IO ()) -> Packer)
-> (Addr# -> IO () -> (Addr# -> IO ()) -> IO ()) -> Packer
forall a b. (a -> b) -> a -> b
$ \Addr#
buf0 IO ()
fails Addr# -> IO ()
k -> Addr# -> IO () -> (Addr# -> IO ()) -> IO ()
fa Addr#
buf0 IO ()
fails (\Addr#
buf1 -> Addr# -> IO () -> (Addr# -> IO ()) -> IO ()
fb Addr#
buf1 IO ()
fails Addr# -> IO ()
k)
 {-# INLINE mappend #-}


-- | Pack data into the given buffer.
--
--   PRECONDITION: The buffer needs to be big enough to hold the packed data,
--   otherwise you'll corrupt the heap (bad). Use `packedSize` to work out
--   how big it needs to be.
--
unsafeRunPacker
        :: Packer       -- ^ Packer to run.
        -> F.Ptr Word8  -- ^ Start of buffer.
        -> IO (Maybe (F.Ptr Word8))
                        -- ^ Pointer to the byte after the last one written.

unsafeRunPacker :: Packer -> Ptr Word8 -> IO (Maybe (Ptr Word8))
unsafeRunPacker (Packer Addr# -> IO () -> (Addr# -> IO ()) -> IO ()
make) (Ptr Addr#
addr)
 = do   IORef (Maybe (Ptr Word8))
ref     <- Maybe (Ptr Word8) -> IO (IORef (Maybe (Ptr Word8)))
forall a. a -> IO (IORef a)
newIORef Maybe (Ptr Word8)
forall a. Maybe a
Nothing

        Addr# -> IO () -> (Addr# -> IO ()) -> IO ()
make Addr#
addr
          (() -> IO ()
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ())
          (\Addr#
addr' -> IORef (Maybe (Ptr Word8)) -> Maybe (Ptr Word8) -> IO ()
forall a. IORef a -> a -> IO ()
writeIORef IORef (Maybe (Ptr Word8))
ref (Ptr Word8 -> Maybe (Ptr Word8)
forall a. a -> Maybe a
Just (Addr# -> Ptr Word8
forall a. Addr# -> Ptr a
Ptr Addr#
addr')))

        IORef (Maybe (Ptr Word8)) -> IO (Maybe (Ptr Word8))
forall a. IORef a -> IO a
readIORef IORef (Maybe (Ptr Word8))
ref
{-# INLINE unsafeRunPacker #-}