packages feed

z85-0.0.0: src/Data/ByteString/Z85.hs

{-# LANGUAGE
    OverloadedStrings
  #-}

module Data.ByteString.Z85 where

import qualified Pipes.Z85 as PZ
import qualified Pipes.ByteString as PB
import qualified Pipes.Text as PT
import Pipes ((>->), Producer, Effect, for, runEffect)

import qualified Data.ByteString as BS
import Data.ByteString.Lazy (ByteString)
import qualified Data.ByteString.Lazy as LBS
import qualified Data.Text as T
import Data.Text.Lazy (Text)
import qualified Data.Text.Lazy as LT
import Data.IORef (readIORef, newIORef, modifyIORef)
import Control.Monad.IO.Class (liftIO)
import Control.Exception (try, SomeException)
import System.IO.Unsafe (unsafePerformIO)



data Z85Error
  = TextNotMod5
  | BSNotMod4
  | ParsingError PZ.Z85ParsingError
  deriving (Eq, Show)


-- | Fails by checking the length is @mod 4@ - warning, checking length on a potentially infinite stream may not be possible, or may
-- cause faulty memory management. Use 'encode\'' if you want to verify offsets after.
encode :: ByteString -> Either Z85Error Text
encode bs
  | LBS.length bs `mod` 4 /= 0 = Left BSNotMod4
  | otherwise = unsafePerformIO $ do
    let go :: IO Text
        go = do
          leftoverRef <- newIORef ""
          let encoded :: Producer T.Text IO ()
              encoded = PB.fromLazy bs >-> PZ.encode leftoverRef
          PT.toLazyM encoded
    eX <- try go
    pure $ case eX of
      Left e -> Left (ParsingError e)
      Right x -> Right x

-- | Fails by checking the residual unparsed input after doing all the work -
-- use 'encode' if you want to check length early.
encode' :: ByteString -> Either Z85Error Text
encode' bs = unsafePerformIO $ do
  let go :: IO (Either Z85Error Text)
      go = do
        leftoverRef <- newIORef ""
        let encoded :: Producer T.Text IO ()
            encoded = PB.fromLazy bs >-> PZ.encode leftoverRef
        x <- PT.toLazyM encoded
        leftover <- readIORef leftoverRef
        pure $
          if BS.length leftover /= 0
          then Left BSNotMod4
          else Right x
  eX <- try go
  pure $ case eX of
    Left e -> Left (ParsingError e)
    Right x -> x



-- | Fails by checking the length is @mod 5@ - warning, checking length on a potentially infinite stream may not be possible, or may
-- cause faulty memory management. Use 'decode\'' if you want to verify offsets after.
decode :: Text -> Either Z85Error ByteString
decode t
  | LT.length t `mod` 5 /= 0 = Left TextNotMod5
  | otherwise = unsafePerformIO $ do
    let go :: IO ByteString
        go = do
          leftoverRef <- newIORef ""
          let decoded :: Producer BS.ByteString IO ()
              decoded = PT.fromLazy t >-> PZ.decode leftoverRef
          PB.toLazyM decoded
    eX <- try go
    pure $ case eX of
      Left e -> Left (ParsingError e)
      Right x -> Right x

-- | Fails by checking the residual unparsed input after doing all the work -
-- use 'decode' if you want to check length early.
decode' :: Text -> Either Z85Error ByteString
decode' t = unsafePerformIO $ do
  let go :: IO (Either Z85Error ByteString)
      go = do
        leftoverRef <- newIORef ""
        let decoded :: Producer BS.ByteString IO ()
            decoded = PT.fromLazy t >-> PZ.decode leftoverRef
        x <- PB.toLazyM decoded
        leftover <- readIORef leftoverRef
        pure $
          if T.length leftover /= 0
          then Left TextNotMod5
          else Right x
  eX <- try go
  pure $ case eX of
    Left e -> Left (ParsingError e)
    Right x -> x