packages feed

circe-0.1.0.0: lib/Codec/Circe/CRC32.hs

-- | CRC32 utilities
module Codec.Circe.CRC32
  ( crc32
  , crc32WithTable
  , crc32Unsafe
  , crc32Table
  ) where

import Codec.Circe.Cfg
import Codec.Circe.Reflect
import Data.Bits
import Data.ByteString.Lazy (ByteString)
import qualified Data.ByteString.Lazy as BS
import Data.Vector.Storable (Vector)
import qualified Data.Vector.Storable as V
import Data.Function
import Data.Monoid
import Data.Semigroup
import Data.Word

-- | calculate CRC32 using lookup table generated from config
crc32 :: CRC32Cfg -> ByteString -> Word32
crc32 cfg = crc32WithTable t cfg
  where
    t = V.fromList $ crc32Table $ crcPoly cfg

-- | calculate CRC32 with precalculated poly table.
crc32WithTable :: Vector Word32 -> CRC32Cfg -> ByteString -> Word32
crc32WithTable t cfg =
  xor (crcFinXor cfg)
    . applyWhen (crcRefOut cfg) ref32
    . crc32Unsafe t (crcRefIn cfg) (crcInit cfg)

-- | calculate CRC32 using an efficient lookup table and init value
crc32Unsafe :: Vector Word32 -> Bool -> Word32 -> ByteString -> Word32
crc32Unsafe t refIn = BS.foldl' go
  where
    go crc b = crc `shiftL` 8 `xor` t `V.unsafeIndex` fromIntegral pos
      where
        pos = fromIntegral ((crc `xor` (fromIntegral b' `shiftL` 24)) `shiftR` 24) :: Word8
          where
            b' = applyWhen refIn ref8 b

-- | generate lookup table using polynomial
crc32Table :: Word32 -> [Word32]
crc32Table poly = calc <$> [0..255]
  where
    calc = appEndo (stimes (8 :: Int) $ Endo step) . (`shiftL` 24)
      where
        step curByte = applyWhen (testBit curByte 31) (`xor` poly) $ curByte `shiftL` 1