packages feed

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

{-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE BlockArguments, LambdaCase, OverloadedStrings #-}
{-# LANGUAGE ExplicitForAll #-}
{-# LANGUAGE RequiredTypeArguments #-}
{-# LANGUAGE FlexibleContexts #-}
{-# OPTIONS_GHC -Wall -fno-warn-tabs #-}

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

	-- * PIPE AND STATE

	runCrc32, crc32, crc32', resetCrc32, compCrc32,

	-- * NO PIPE

	crc32StepBS, crc32StepBS', initialCrc32, complementCrc32,

	-- * TYPE

	Crc32(..),

	crc32ToByteString, crc32ToByteStringBE,
	crc32FromByteString, crc32FromByteStringBE

) 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.Word
import Data.ByteString.Lazy qualified as LBS

crc32 :: forall nm ->
	(U.Member Pipe.P es, U.Member (State.Named nm Crc32) es) =>
	Eff.E es LBS.ByteString LBS.ByteString r
crc32 nm = 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 LBS.ByteString LBS.ByteString r
crc32Body nm = fix \go -> Pipe.await >>= \bs -> do
	State.modifyN nm \(Crc32 c) -> Crc32 $ c `crc32StepBS` bs
	Pipe.yield bs
	go

crc32' :: forall nm ->
	(U.Member Pipe.P es, U.Member (State.Named nm Crc32) es) =>
	Eff.E es LBS.ByteString LBS.ByteString ()
crc32' nm = 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 LBS.ByteString LBS.ByteString ()
crc32Body' nm = fix \go -> Pipe.awaitMaybe >>= \case
	Nothing -> pure ()
	Just bs -> do
		State.modifyN nm \(Crc32 c) -> Crc32 $! c `crc32StepBS` bs
		Pipe.yield bs
		go

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

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

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

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

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

crc32FromByteStringBE :: LBS.ByteString -> Maybe Crc32
crc32FromByteStringBE = (Crc32 <$>) . go (4 :: Int) 0 . LBS.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