packages feed

snappy-c-0.1.0: src/Codec/Compression/SnappyC/Internal/Checksum.hs

-- | Intended for qualified import:
--
-- > import Codec.Compression.SnappyC.Internal.Checksum (Checksum)
-- > import Codec.Compression.SnappyC.Internal.Checksum qualified as Checksum

module Codec.Compression.SnappyC.Internal.Checksum
  ( -- * Type
    Checksum -- Opaque

    -- ** Calculating checksums
  , calculate

    -- ** Encoding and decoding checksums
  , encode
  , decode
  ) where

import Data.ByteString qualified as Strict (ByteString)
import Data.ByteString qualified as BS.Strict

import Data.Bits
import Data.Digest.CRC32C
import Data.Word

-- | Masked CRC32C checksum
newtype Checksum = Checksum { getChecksum :: Word32 }
  deriving newtype (Show, Eq)

-- | Calculate
calculate :: Strict.ByteString -> Checksum
calculate =
    Checksum . maskChecksum . crc32c
  where
    maskChecksum :: Word32 -> Word32
    maskChecksum = (+ 0xa282ead8) . (`rotateR` 15)

-- | Encode
encode :: Checksum -> Strict.ByteString
encode =
    BS.Strict.pack . toLittleEndian . getChecksum
  where
    toLittleEndian :: Word32 -> [Word8]
    toLittleEndian s =
      [ fromIntegral          (s .&. 0x000000ff)
      , fromIntegral $ shiftR (s .&. 0x0000ff00) 8
      , fromIntegral $ shiftR (s .&. 0x00ff0000) 16
      , fromIntegral $ shiftR (s .&. 0xff000000) 24
      ]

-- | Decode
--
-- __Precondition:__ Input must be exactly 4 bytes.
decode :: Strict.ByteString -> Checksum
decode =
    Checksum . fromLittleEndian . BS.Strict.unpack
  where
    fromLittleEndian :: [Word8] -> Word32
    fromLittleEndian [b1, b2, b3, b4] =
            fromIntegral b1
        .|. fromIntegral b2 `shiftL` 8
        .|. fromIntegral b3 `shiftL` 16
        .|. fromIntegral b4 `shiftL` 24
    fromLittleEndian _ = error "Checksum.decode: precondition violated"