module Rattletrap.Utility.Crc
  ( getCrc32
  )
where

import Rattletrap.Data

import qualified Data.Bits as Bits
import qualified Data.ByteString as Bytes
import qualified Data.IntMap as IntMap
import qualified Data.Word as Word

-- | Computes the CRC32 of some bytes. This is done to ensure that the bytes
-- are valid before trying to parse them.
--
-- @
-- getCrc32 ('Data.ByteString.Lazy.pack' [0x00])
-- @
--
-- This CRC uses an initial value of @0xefcbf201@ and a polynomial of
-- @0x04c11db7@.
getCrc32 :: Bytes.ByteString -> Word.Word32
getCrc32 :: ByteString -> Word32
getCrc32 ByteString
bytes = do
  let
    update :: Word32 -> Word8 -> Word32
update = IntMap Word32 -> Word32 -> Word8 -> Word32
crc32Update IntMap Word32
crc32Table
    initial :: Word32
initial = Word32 -> Word32
forall a. Bits a => a -> a
Bits.complement Word32
crc32Initial
    crc :: Word32
crc = (Word32 -> Word8 -> Word32) -> Word32 -> ByteString -> Word32
forall a. (a -> Word8 -> a) -> a -> ByteString -> a
Bytes.foldl Word32 -> Word8 -> Word32
update Word32
initial ByteString
bytes
  Word32 -> Word32
forall a. Bits a => a -> a
Bits.complement Word32
crc

crc32Update
  :: IntMap.IntMap Word.Word32 -> Word.Word32 -> Word.Word8 -> Word.Word32
crc32Update :: IntMap Word32 -> Word32 -> Word8 -> Word32
crc32Update IntMap Word32
table Word32
crc Word8
byte = do
  let
    toWord8 :: (Integral a) => a -> Word.Word8
    toWord8 :: a -> Word8
toWord8 = a -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral
    toInt :: (Integral a) => a -> Int
    toInt :: a -> Int
toInt = a -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral
    index :: Int
index = Word8 -> Int
forall a. Integral a => a -> Int
toInt (Word8 -> Word8 -> Word8
forall a. Bits a => a -> a -> a
Bits.xor Word8
byte (Word32 -> Word8
forall a. Integral a => a -> Word8
toWord8 (Word32 -> Int -> Word32
forall a. Bits a => a -> Int -> a
Bits.shiftR Word32
crc Int
24)))
    left :: Word32
left = IntMap Word32
table IntMap Word32 -> Int -> Word32
forall a. IntMap a -> Int -> a
IntMap.! Int
index
    right :: Word32
right = Word32 -> Int -> Word32
forall a. Bits a => a -> Int -> a
Bits.shiftL Word32
crc Int
8
  Word32 -> Word32 -> Word32
forall a. Bits a => a -> a -> a
Bits.xor Word32
left Word32
right

crc32Initial :: Word.Word32
crc32Initial :: Word32
crc32Initial = Word32
0xefcbf201

crc32Table :: IntMap.IntMap Word.Word32
crc32Table :: IntMap Word32
crc32Table = [(Int, Word32)] -> IntMap Word32
forall a. [(Int, a)] -> IntMap a
IntMap.fromDistinctAscList ([Int] -> [Word32] -> [(Int, Word32)]
forall a b. [a] -> [b] -> [(a, b)]
zip [Int
0 ..] [Word32]
forall a. Integral a => [a]
rawCrc32Table)