packages feed

dahdit-0.5.0: src/Dahdit/Iface.hs

module Dahdit.Iface
  ( BinaryGetTarget (..)
  , getTarget
  , BinaryPutTarget (..)
  , putTarget
  , MutBinaryPutTarget (..)
  , mutPutTargetOffset
  , mutPutTarget
  , getEnd
  , decode
  , decodeInc
  , decodeEnd
  , decodeFile
  , decodeFileEnd
  , encode
  , encodeFile
  , mutEncode
  )
where

import Control.Monad (unless, (>=>))
import Control.Monad.Primitive (MonadPrim, PrimMonad (..), RealWorld)
import Dahdit.Binary (Binary (..))
import Dahdit.Free (Get, Put)
import Dahdit.Funs (getRemainingSize)
import Dahdit.Mem (MemPtr (..), MutableMem (..), emptyMemPtr, mutViewVecMem, viewBSMem, viewSBSMem, viewVecMem, withBAMem, withBSMem, withSBSMem, withVecMem)
import Dahdit.Run (GetError, GetIncCb, GetIncChunk (..), newGetIncEnv, runCount, runGetIncInternal, runGetInternal, runPutInternal)
import Dahdit.Sizes (ByteCount (..))
import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as BSC
import Data.ByteString.Short (ShortByteString)
import Data.Coerce (coerce)
import Data.Primitive.ByteArray (ByteArray, MutableByteArray, emptyByteArray, sizeofByteArray)
import Data.Text (Text)
import qualified Data.Text.Encoding as TE
import Data.Vector.Storable (Vector)
import qualified Data.Vector.Storable as VS
import Data.Vector.Storable.Mutable (IOVector)
import Data.Word (Word8)

-- | Abstracts over the sources we can read from.
class (PrimMonad m) => BinaryGetTarget z m where
  -- | Get a value from the source given a starting offset, returning a result and final offset.
  -- On error, the offset will indicate where in the source the error occurred.
  getTargetOffset :: ByteCount -> Get a -> z -> m (Either GetError a, ByteCount)

  -- | Get a value incrementally from sources yielded by the given callback, returning a
  -- result, final offset in the whole stream, and final offset in the current chunk.
  -- Takes an optional maximum capacity.
  getTargetInc :: Maybe ByteCount -> Get a -> GetIncCb z m -> m (Either GetError a, ByteCount, ByteCount)

-- | Get a value from the source, returning a result and final offset.
getTarget :: (BinaryGetTarget z m) => Get a -> z -> m (Either GetError a, ByteCount)
getTarget = getTargetOffset 0

-- | Abstracts over the immutable sinks we can render to.
class (BinaryGetTarget z m) => BinaryPutTarget z m where
  -- | Put an action to the sink with the given length.
  -- Prefer 'putTarget' to safely count capacity, or use 'encode' to use byte size.
  putTargetUnsafe :: Put -> ByteCount -> m z

-- | Put an action to the sink with calculated capacity.
putTarget :: (BinaryPutTarget z m) => Put -> m z
putTarget p = putTargetUnsafe p (runCount p)

class (BinaryGetTarget z m) => MutBinaryPutTarget z m where
  mutPutTargetOffsetUnsafe :: ByteCount -> Put -> ByteCount -> z -> m ByteCount

mutPutTargetOffset :: (MutBinaryPutTarget z m) => ByteCount -> Put -> z -> m ByteCount
mutPutTargetOffset off p = mutPutTargetOffsetUnsafe off p (runCount p)

mutPutTarget :: (MutBinaryPutTarget z m) => Put -> z -> m ByteCount
mutPutTarget = mutPutTargetOffset 0

instance BinaryGetTarget String IO where
  getTargetOffset = runGetString
  getTargetInc = runGetIncString

instance BinaryPutTarget String IO where
  putTargetUnsafe = runPutString

instance BinaryGetTarget Text IO where
  getTargetOffset = runGetText
  getTargetInc = runGetIncText

instance BinaryPutTarget Text IO where
  putTargetUnsafe = runPutText

instance (PrimMonad m) => BinaryGetTarget ShortByteString m where
  getTargetOffset = runGetSBS
  getTargetInc = runGetIncSBS

instance (PrimMonad m) => BinaryPutTarget ShortByteString m where
  putTargetUnsafe = runPutSBS

instance BinaryGetTarget ByteString IO where
  getTargetOffset = runGetBS
  getTargetInc = runGetIncBS

instance BinaryPutTarget ByteString IO where
  putTargetUnsafe = runPutBS

instance (PrimMonad m) => BinaryGetTarget ByteArray m where
  getTargetOffset = runGetBA
  getTargetInc = runGetIncBA

instance (PrimMonad m) => BinaryPutTarget ByteArray m where
  putTargetUnsafe = runPutBA

instance BinaryGetTarget (Vector Word8) IO where
  getTargetOffset = runGetVec
  getTargetInc = runGetIncVec

instance BinaryPutTarget (Vector Word8) IO where
  putTargetUnsafe = runPutVec

instance (MonadPrim s m) => BinaryGetTarget (MutableByteArray s) m where
  getTargetOffset bc g z = unsafeUseFrozenMem z (getTargetOffset bc g)
  getTargetInc mbc g cb = getTargetInc mbc g (cb >=> maybe (pure Nothing) (fmap Just . unsafeFreezeMem))

instance (MonadPrim s m) => MutBinaryPutTarget (MutableByteArray s) m where
  mutPutTargetOffsetUnsafe = runMutPutBA

instance BinaryGetTarget (IOVector Word8) IO where
  getTargetOffset bc g z = unsafeUseFrozenMem z (getTargetOffset bc g)
  getTargetInc mbc g cb = getTargetInc mbc g (cb >=> maybe (pure Nothing) (fmap Just . unsafeFreezeMem))

instance MutBinaryPutTarget (IOVector Word8) IO where
  mutPutTargetOffsetUnsafe = runMutPutVec

-- | Wrapper that asserts a get action has consumed to the end.
getEnd :: Get a -> Get a
getEnd getter = do
  a <- getter
  b <- getRemainingSize
  unless (b == 0) (fail ("Expected end of input but had bytes remaining: " ++ show (unByteCount b)))
  pure a

-- | Decode a value from a source returning a result and consumed byte count.
decode :: (Binary a, BinaryGetTarget z m) => z -> m (Either GetError a, ByteCount)
decode = getTarget get

-- | Decode a value incrementally from sources yielded by a callback.
decodeInc :: (Binary a, BinaryGetTarget z m) => Maybe ByteCount -> GetIncCb z m -> m (Either GetError a, ByteCount, ByteCount)
decodeInc mayCap = getTargetInc mayCap get

-- | 'decode' but expect the end of input.
decodeEnd :: (Binary a, BinaryGetTarget z m) => z -> m (Either GetError a, ByteCount)
decodeEnd = getTarget (getEnd get)

-- | Decode a value from a file.
decodeFile :: (Binary a) => FilePath -> IO (Either GetError a, ByteCount)
decodeFile = runGetFile get

-- | 'decodeFile' but expect the end of file.
decodeFileEnd :: (Binary a) => FilePath -> IO (Either GetError a, ByteCount)
decodeFileEnd = runGetFile (getEnd get)

-- | Encode a value to a sink.
encode :: (Binary a, BinaryPutTarget z m) => a -> m z
encode a = putTargetUnsafe (put a) (byteSize a)

-- | Encode a value to a file.
encodeFile :: (Binary a) => a -> FilePath -> IO ()
encodeFile a = runPutFile (put a) (byteSize a)

-- | Encode a value to a mutable buffer, returning number of bytes filled.
mutEncode :: (Binary a, MutBinaryPutTarget z m) => a -> z -> m ByteCount
mutEncode a = mutPutTargetOffsetUnsafe 0 (put a) (byteSize a)

runGetString :: ByteCount -> Get a -> String -> IO (Either GetError a, ByteCount)
runGetString off act = runGetBS off act . BSC.pack

runGetText :: ByteCount -> Get a -> Text -> IO (Either GetError a, ByteCount)
runGetText off act = runGetBS off act . TE.encodeUtf8

runGetBA :: (PrimMonad m) => ByteCount -> Get a -> ByteArray -> m (Either GetError a, ByteCount)
runGetBA off act ba = runGetInternal off act (coerce (sizeofByteArray ba)) ba

runGetSBS :: (PrimMonad m) => ByteCount -> Get a -> ShortByteString -> m (Either GetError a, ByteCount)
runGetSBS off act = runGetBA off act . viewSBSMem

runGetBS :: ByteCount -> Get a -> ByteString -> IO (Either GetError a, ByteCount)
runGetBS off act bs = runGetInternal off act (coerce (BS.length bs)) (viewBSMem bs)

runGetVec :: ByteCount -> Get a -> Vector Word8 -> IO (Either GetError a, ByteCount)
runGetVec off act vec = runGetInternal off act (coerce (VS.length vec)) (viewVecMem vec)

runGetFile :: Get a -> FilePath -> IO (Either GetError a, ByteCount)
runGetFile act fp = do
  bs <- BS.readFile fp
  runGetBS 0 act bs

runGetIncString :: Maybe ByteCount -> Get a -> GetIncCb String IO -> IO (Either GetError a, ByteCount, ByteCount)
runGetIncString mayCap g cb = runGetIncBS mayCap g (fmap (fmap BSC.pack) . cb)

runGetIncText :: Maybe ByteCount -> Get a -> GetIncCb Text IO -> IO (Either GetError a, ByteCount, ByteCount)
runGetIncText mayCap g cb = runGetIncBS mayCap g (fmap (fmap TE.encodeUtf8) . cb)

runGetIncBA :: (PrimMonad m) => Maybe ByteCount -> Get a -> GetIncCb ByteArray m -> m (Either GetError a, ByteCount, ByteCount)
runGetIncBA mayCap act cb = do
  env <- newGetIncEnv mayCap (GetIncChunk 0 0 emptyByteArray)
  let view s = GetIncChunk 0 (coerce (sizeofByteArray s)) s
  let cb' = fmap (fmap view) . cb
  runGetIncInternal act env cb'

runGetIncSBS :: (PrimMonad m) => Maybe ByteCount -> Get a -> GetIncCb ShortByteString m -> m (Either GetError a, ByteCount, ByteCount)
runGetIncSBS mayCap act cb = runGetIncBA mayCap act (fmap (fmap viewSBSMem) . cb)

runGetIncMemPtr :: Maybe ByteCount -> Get a -> GetIncCb (MemPtr RealWorld) IO -> IO (Either GetError a, ByteCount, ByteCount)
runGetIncMemPtr mayCap act cb = do
  mem <- emptyMemPtr
  env <- newGetIncEnv mayCap (GetIncChunk 0 0 mem)
  let view mem'@(MemPtr _ off len) = GetIncChunk 0 (len - off) mem'
  let cb' = fmap (fmap view) . cb
  runGetIncInternal act env cb'

runGetIncBS :: Maybe ByteCount -> Get a -> GetIncCb ByteString IO -> IO (Either GetError a, ByteCount, ByteCount)
runGetIncBS mayCap act cb = runGetIncMemPtr mayCap act (fmap (fmap viewBSMem) . cb)

runGetIncVec :: Maybe ByteCount -> Get a -> GetIncCb (Vector Word8) IO -> IO (Either GetError a, ByteCount, ByteCount)
runGetIncVec mayCap act cb = runGetIncMemPtr mayCap act (fmap (fmap viewVecMem) . cb)

runPutString :: Put -> ByteCount -> IO String
runPutString act len = fmap BSC.unpack (runPutBS act len)

runPutText :: Put -> ByteCount -> IO Text
runPutText act len = fmap TE.decodeUtf8 (runPutBS act len)

runPutBA :: (PrimMonad m) => Put -> ByteCount -> m ByteArray
runPutBA act len = withBAMem len (runPutInternal 0 act len)

runPutSBS :: (PrimMonad m) => Put -> ByteCount -> m ShortByteString
runPutSBS act len = withSBSMem len (runPutInternal 0 act len)

runPutBS :: Put -> ByteCount -> IO ByteString
runPutBS act len = withBSMem len (runPutInternal 0 act len)

runPutVec :: Put -> ByteCount -> IO (Vector Word8)
runPutVec act len = withVecMem len (runPutInternal 0 act len)

runPutFile :: Put -> ByteCount -> FilePath -> IO ()
runPutFile act cap fp = do
  bs <- runPutBS act cap
  BS.writeFile fp bs

runMutPutBA :: (MonadPrim s m) => ByteCount -> Put -> ByteCount -> MutableByteArray s -> m ByteCount
runMutPutBA = runPutInternal

runMutPutVec :: ByteCount -> Put -> ByteCount -> IOVector Word8 -> IO ByteCount
runMutPutVec off act len mvec = runPutInternal off act len (mutViewVecMem mvec)