fs-api-0.3.0.0: src/System/FS/CRC.hs
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE ScopedTypeVariables #-}
-- | Support for CRC
module System.FS.CRC (
-- * Wrap digest functionality
CRC (..)
, computeCRC
, initCRC
, updateCRC
-- * File system functions with CRC functionality
, hGetAllAtCRC
, hGetExactlyAtCRC
, hPutAllCRC
) where
import Control.Monad (foldM)
import Control.Monad.Class.MonadThrow
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BL
import Data.Coerce
import qualified Data.Digest.CRC32 as Digest
import Data.Word
import Foreign.Storable (Storable)
import GHC.Generics (Generic)
import GHC.Stack
import System.FS.API.Lazy
import System.FS.API.Strict
{-------------------------------------------------------------------------------
Wrap functionality from digest
-------------------------------------------------------------------------------}
newtype CRC = CRC { getCRC :: Word32 }
deriving (Eq, Show, Generic, Storable)
initCRC :: CRC
initCRC = CRC $ Digest.crc32 ([] :: [Word8])
updateCRC :: forall a. Digest.CRC32 a => a -> CRC -> CRC
updateCRC = coerce (flip (Digest.crc32Update :: Word32 -> a -> Word32))
computeCRC :: forall a. Digest.CRC32 a => a -> CRC
computeCRC = coerce (Digest.crc32 :: a -> Word32)
{-------------------------------------------------------------------------------
File system functions that compute CRCs
-------------------------------------------------------------------------------}
-- | Variation on 'hPutAll' that also computes a CRC
hPutAllCRC :: forall m h. (HasCallStack, Monad m)
=> HasFS m h
-> Handle h
-> BL.ByteString
-> m (Word64, CRC)
hPutAllCRC hasFS h = foldM putChunk (0, initCRC) . BL.toChunks
where
putChunk :: (Word64, CRC) -> BS.ByteString -> m (Word64, CRC)
putChunk (written, crc) chunk = do
chunkSize <- hPutAllStrict hasFS h chunk
let !written' = written + chunkSize
!crc' = updateCRC chunk crc
return (written', crc')
-- | Variation on 'hGetExactlyAt' that also computes a CRC
hGetExactlyAtCRC :: forall m h. (HasCallStack, MonadThrow m)
=> HasFS m h
-> Handle h
-> Word64 -- ^ The number of bytes to read.
-> AbsOffset -- ^ The offset at which to read.
-> m (BL.ByteString, CRC)
hGetExactlyAtCRC hasFS h n offset = do
-- TODO Interleave reading with computing the CRC. Better cache locality
-- and fits better with incremental parsing, when we add support for that.
bs <- hGetExactlyAt hasFS h n offset
let !crc = computeCRC bs
return (bs, crc)
-- | Variation on 'hGetAllAt' that also computes a CRC
hGetAllAtCRC :: forall m h. Monad m
=> HasFS m h
-> Handle h
-> AbsOffset -- ^ The offset at which to read.
-> m (BL.ByteString, CRC)
hGetAllAtCRC hasFS h offset = do
-- TODO Interleave reading with computing the CRC. Better cache locality
-- and fits better with incremental parsing, when we add support for that.
bs <- hGetAllAt hasFS h offset
let !crc = computeCRC bs
return (bs, crc)