packages feed

telescope-0.2.0: src/Telescope/Fits/Checksum.hs

module Telescope.Fits.Checksum where

import Data.Bits (complement, shiftR, (.&.))
import Data.ByteString.Internal
import Data.Fits (Value (..))
import Data.Text (Text, pack)
import Data.Word
import Foreign.C.String
import Foreign.C.Types
import Foreign.ForeignPtr (withForeignPtr)
import Foreign.Ptr
import GHC.IO


-- | Generate the Checksum per the FITS spec
checksum :: ByteString -> Checksum
checksum bs = unsafePerformIO $ do
  let (fptr, offset, len) = toForeignPtr bs
  withForeignPtr fptr $ \ptr -> do
    ci <- c_checksum (ptr `plusPtr` offset) (fromIntegral len)
    pure $ Checksum $ fromIntegral ci


-- | Encode the Checksum as ASCII chars per FITS spec
encodeChecksum :: Checksum -> Text
encodeChecksum (Checksum csum) =
  unsafePerformIO $ do
    let comp = complement csum
    let str = replicate 16 ' '
    out <- withCString str $ \cs -> do
      c_char_encode (fromIntegral comp) cs
      peekCString cs
    pure $ pack out


foreign import ccall "checksum" c_checksum :: Ptr CChar -> CInt -> IO CUInt


newtype Checksum = Checksum Word32
  deriving (Eq, Show)


instance Semigroup Checksum where
  Checksum w1 <> Checksum w2 = Checksum (add1s w1 w2)


checksumValue :: Checksum -> Value
checksumValue (Checksum s) = String (pack (show s))


foreign import ccall "char_encode" c_char_encode :: CUInt -> CString -> IO ()


add1s :: Word32 -> Word32 -> Word32
add1s x y =
  let
    sum64 = (+) @Word64 (fromIntegral x) (fromIntegral y)
    result =
      if sum64 > maxWord32
        then (sum64 .&. maxWord32) + (sum64 `shiftR` 32)
        else sum64
   in
    fromIntegral result
 where
  maxWord32 :: Word64
  maxWord32 = 0xFFFFFFFF