packages feed

yaftee-conduit-bytestring-0.1.0.0: src/Control/Monad/Yaftee/Pipe/ByteString/Crc.hs

{-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE BlockArguments, OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE RequiredTypeArguments #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE FlexibleContexts #-}
{-# OPTIONS_GHC -Wall -fno-warn-tabs #-}

module Control.Monad.Yaftee.Pipe.ByteString.Crc (

	-- * PIPE

	runCrc32, crc32, crc32', resetCrc32, compCrc32,

	-- * NO PIPE

	crc32StepBS, crc32StepBS', initialCrc32, complementCrc32,

	-- * TYPE

	Crc32(..),

	crc32ToByteString, byteStringToCrc32,
	crc32ToByteStringBE, byteStringToCrc32BE,

	) where

import Control.Monad.Fix
import Control.Monad.Yaftee.Eff qualified as Eff
import Control.Monad.Yaftee.Pipe qualified as Pipe
import Control.Monad.Yaftee.Pipe.ByteString.Crc.Common
import Control.Monad.Yaftee.State qualified as State
import Control.HigherOpenUnion qualified as U
import Data.Bits
import Data.Bool
import Data.Word
import Data.ByteString qualified as BS

crc32 :: forall nm -> (U.Member Pipe.P es, U.Member (State.Named nm Crc32) es) =>
	Eff.E es BS.ByteString BS.ByteString r
crc32 nm = do
	State.putN nm $ Crc32 0xffffffff
	crc32Body nm

crc32' :: forall nm -> (U.Member Pipe.P es, U.Member (State.Named nm Crc32) es) =>
	Eff.E es BS.ByteString BS.ByteString ()
crc32' nm = do
	State.putN nm $ Crc32 0xffffffff
	crc32Body' nm

crc32Body :: forall nm -> (U.Member Pipe.P es, U.Member (State.Named nm Crc32) es) =>
	Eff.E es BS.ByteString BS.ByteString r
crc32Body nm = fix \go -> Pipe.await >>= \bs -> do
	State.modifyN nm \(Crc32 c) -> Crc32 $ c `crc32StepBS` bs
	Pipe.yield bs
	go

crc32Body' :: forall es . forall nm -> (U.Member Pipe.P es, U.Member (State.Named nm Crc32) es) =>
	Eff.E es BS.ByteString BS.ByteString ()
crc32Body' nm = fix \go ->
	Pipe.isMore >>= bool (pure ()) (Pipe.await >>= \bs -> do
		State.modifyN nm \(Crc32 c) -> Crc32 $! c `crc32StepBS` bs
		Pipe.yield bs
		go)

crc32ToByteString :: Crc32 -> BS.ByteString
crc32ToByteString (Crc32 c) = bs `BS.append` BS.replicate (4 - BS.length bs) 0
	where
	bs = numToBs c
	numToBs 0 = ""
	numToBs n = fromIntegral (n .&. 0xff) `BS.cons` numToBs (n `shiftR` 8)

crc32ToByteStringBE :: Crc32 -> BS.ByteString
crc32ToByteStringBE (Crc32 c) = BS.replicate (4 - BS.length bs) 0 `BS.append` bs
	where
	bs = numToBs c ""
	numToBs 0 b = b
	numToBs n b = numToBs (n `shiftR` 8) (fromIntegral (n .&. 0xff) `BS.cons` b)

byteStringToCrc32 :: BS.ByteString -> Maybe Crc32
byteStringToCrc32 = (Crc32 <$>) . go (4 :: Int) . BS.unpack
	where
	go 0 [] = Just 0
	go n (w : ws)
		| n > 0 = (fromIntegral w .|.) . (`shiftL` 8) <$> go (n - 1) ws
	go _ _ = Nothing

byteStringToCrc32BE :: BS.ByteString -> Maybe Crc32
byteStringToCrc32BE = (Crc32 <$>) . go (4 :: Int) 0 . BS.unpack
	where
	go 0 s [] = Just s
	go n s (w : ws)
		| n > 0 =  go (n - 1) (fromIntegral w .|. s `shiftL` 8) ws
	go _ _ _ = Nothing

crc32StepBS :: Word32 -> BS.ByteString -> Word32
crc32StepBS = BS.foldl' crc32Step

crc32StepBS' :: Crc32 -> BS.ByteString -> Crc32
crc32StepBS' = (Crc32 .) . BS.foldl' crc32Step . unCrc32