{-# LANGUAGE BangPatterns #-}

-- |
-- Module: Lightning.Protocol.BOLT7.CRC32C
-- Copyright: (c) 2025 Jared Tobin
-- License: MIT
-- Maintainer: Jared Tobin <jared@ppad.tech>
--
-- CRC-32C (Castagnoli) implementation for BOLT #7 checksums.
--
-- This is an internal helper module implementing CRC-32C as specified
-- in RFC 3720. CRC-32C uses the Castagnoli polynomial 0x1EDC6F41.

module Lightning.Protocol.BOLT7.CRC32C (
    crc32c
  ) where

import Data.Bits (shiftR, xor, (.&.))
import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
import Data.Word (Word8, Word32)

-- | CRC-32C polynomial (Castagnoli): 0x1EDC6F41 reflected = 0x82F63B78
crc32cPoly :: Word32
crc32cPoly :: Word32
crc32cPoly = Word32
0x82F63B78
{-# INLINE crc32cPoly #-}

-- | Compute CRC-32C of a bytestring.
--
-- >>> crc32c "123456789"
-- 0xe3069283
crc32c :: ByteString -> Word32
crc32c :: ByteString -> Word32
crc32c = Word32 -> Word32 -> Word32
forall a. Bits a => a -> a -> a
xor Word32
0xFFFFFFFF (Word32 -> Word32)
-> (ByteString -> Word32) -> ByteString -> Word32
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Word32 -> Word8 -> Word32) -> Word32 -> ByteString -> Word32
forall a. (a -> Word8 -> a) -> a -> ByteString -> a
BS.foldl' Word32 -> Word8 -> Word32
updateByte Word32
0xFFFFFFFF
{-# INLINE crc32c #-}

-- | Update CRC with a single byte.
updateByte :: Word32 -> Word8 -> Word32
updateByte :: Word32 -> Word8 -> Word32
updateByte !Word32
crc !Word8
byte =
  let crc' :: Word32
crc' = Word32
crc Word32 -> Word32 -> Word32
forall a. Bits a => a -> a -> a
`xor` Word8 -> Word32
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word8
byte
  in  Int -> Word32 -> Word32
go Int
8 Word32
crc'
  where
    go :: Int -> Word32 -> Word32
    go :: Int -> Word32 -> Word32
go Int
0 !Word32
c = Word32
c
    go !Int
n !Word32
c =
      let c' :: Word32
c' = if Word32
c Word32 -> Word32 -> Word32
forall a. Bits a => a -> a -> a
.&. Word32
1 Word32 -> Word32 -> Bool
forall a. Eq a => a -> a -> Bool
/= Word32
0
               then (Word32
c Word32 -> Int -> Word32
forall a. Bits a => a -> Int -> a
`shiftR` Int
1) Word32 -> Word32 -> Word32
forall a. Bits a => a -> a -> a
`xor` Word32
crc32cPoly
               else Word32
c Word32 -> Int -> Word32
forall a. Bits a => a -> Int -> a
`shiftR` Int
1
      in  Int -> Word32 -> Word32
go (Int
n Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) Word32
c'
{-# INLINE updateByte #-}