-- CRC24.hs: OpenPGP (RFC4880) CRC24 implementation
-- Copyright © 2012-2019 Clint Adams
-- This software is released under the terms of the Expat license.
-- (See the LICENSE file).

module Data.Digest.CRC24 (
   crc24
 , crc24Lazy
) where

import Data.Bits (shiftL, (.&.), xor)
import Data.ByteString.Lazy (ByteString)
import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as BL
import Data.Word (Word8, Word32)

crc24Init :: Word32
crc24Init :: Word32
crc24Init = Word32
0xB704CE

crc24Poly :: Word32
crc24Poly :: Word32
crc24Poly = Word32
0x1864CFB

crc24Update :: Word32 -> Word8 -> Word32
crc24Update :: Word32 -> Word8 -> Word32
crc24Update Word32
c Word8
b = ([Word32] -> Word32
forall a. [a] -> a
last ([Word32] -> Word32)
-> ([Word32] -> [Word32]) -> [Word32] -> Word32
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> [Word32] -> [Word32]
forall a. Int -> [a] -> [a]
take Int
9 ([Word32] -> Word32) -> [Word32] -> Word32
forall a b. (a -> b) -> a -> b
$ (Word32 -> Word32) -> Word32 -> [Word32]
forall a. (a -> a) -> a -> [a]
iterate (\Word32
x -> if Word32 -> Int -> Word32
forall a. Bits a => a -> Int -> a
shiftL Word32
x Int
1 Word32 -> Word32 -> Word32
forall a. Bits a => a -> a -> a
.&. Word32
0x1000000 Word32 -> Word32 -> Bool
forall a. Eq a => a -> a -> Bool
== Word32
0x1000000 then Word32 -> Int -> Word32
forall a. Bits a => a -> Int -> a
shiftL Word32
x Int
1 Word32 -> Word32 -> Word32
forall a. Bits a => a -> a -> a
`xor` Word32
crc24Poly else Word32 -> Int -> Word32
forall a. Bits a => a -> Int -> a
shiftL Word32
x Int
1) (Word32
c Word32 -> Word32 -> Word32
forall a. Bits a => a -> a -> a
`xor` Word32 -> Int -> Word32
forall a. Bits a => a -> Int -> a
shiftL (Word8 -> Word32
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word8
b) Int
16)) Word32 -> Word32 -> Word32
forall a. Bits a => a -> a -> a
.&. Word32
0xFFFFFF

crc24 :: B.ByteString -> Word32
crc24 :: ByteString -> Word32
crc24 ByteString
bs = ByteString -> Word32
crc24Lazy (ByteString -> Word32)
-> ([ByteString] -> ByteString) -> [ByteString] -> Word32
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [ByteString] -> ByteString
BL.fromChunks ([ByteString] -> Word32) -> [ByteString] -> Word32
forall a b. (a -> b) -> a -> b
$ [ByteString
bs]

crc24Lazy :: ByteString -> Word32
crc24Lazy :: ByteString -> Word32
crc24Lazy = (Word32 -> Word8 -> Word32) -> Word32 -> ByteString -> Word32
forall a. (a -> Word8 -> a) -> a -> ByteString -> a
BL.foldl' Word32 -> Word8 -> Word32
crc24Update Word32
crc24Init