packages feed

dahdit-0.2.0: src/Dahdit/Internal.hs

module Dahdit.Internal where

import Data.Bits (Bits (..))
import Data.Int (Int16, Int32, Int64, Int8)
import Data.ShortWord (Int24, Word24)
import Data.Word (Word16, Word32, Word64, Word8)
import GHC.Float (castDoubleToWord64, castFloatToWord32, castWord32ToFloat, castWord64ToDouble)

newtype ViaFromIntegral x y = ViaFromIntegral {unViaFromIntegral :: y}
  deriving newtype (Num)

-- Types that can swap endianness - swapEndian is its own inverse
class Num w => SwapEndian w where
  swapEndian :: w -> w

instance (SwapEndian x, Integral x, Integral y) => SwapEndian (ViaFromIntegral x y) where
  swapEndian = ViaFromIntegral . fromIntegral @x @y . swapEndian . fromIntegral @y @x . unViaFromIntegral

instance SwapEndian Word8 where
  swapEndian = id

instance SwapEndian Int8 where
  swapEndian = id

instance SwapEndian Word16 where
  swapEndian w =
    let (!b0, !b1) = unMkWord16LE w
    in  mkWord16LE b1 b0

deriving via (ViaFromIntegral Word16 Int16) instance SwapEndian Int16

instance SwapEndian Word24 where
  swapEndian w =
    let (!b0, !b1, !b2) = unMkWord24LE w
    in  mkWord24LE b2 b1 b0

deriving via (ViaFromIntegral Word24 Int24) instance SwapEndian Int24

instance SwapEndian Word32 where
  swapEndian w =
    let (!b0, !b1, !b2, !b3) = unMkWord32LE w
    in  mkWord32LE b3 b2 b1 b0

deriving via (ViaFromIntegral Word32 Int32) instance SwapEndian Int32

instance SwapEndian Word64 where
  swapEndian w =
    let (!b0, !b1, !b2, !b3, !b4, !b5, !b6, !b7) = unMkWord64LE w
    in  mkWord64LE b7 b6 b5 b4 b3 b2 b1 b0

deriving via (ViaFromIntegral Word64 Int64) instance SwapEndian Int64

instance SwapEndian Float where
  swapEndian w =
    let (!b0, !b1, !b2, !b3) = unMkFloatLE w
    in  mkFloatLE b3 b2 b1 b0

instance SwapEndian Double where
  swapEndian w =
    let (!b0, !b1, !b2, !b3, !b4, !b5, !b6, !b7) = unMkDoubleLE w
    in  mkDoubleLE b7 b6 b5 b4 b3 b2 b1 b0

mkWord16LE :: Word8 -> Word8 -> Word16
mkWord16LE b0 b1 = (fromIntegral b1 `unsafeShiftL` 8) .|. fromIntegral b0

unMkWord16LE :: Word16 -> (Word8, Word8)
unMkWord16LE w =
  let b0 = fromIntegral w
      b1 = fromIntegral (w `shiftR` 8)
  in  (b0, b1)

mkWord24LE :: Word8 -> Word8 -> Word8 -> Word24
mkWord24LE b0 b1 b2 = fromIntegral (mkWord32LE b0 b1 b2 0)

unMkWord24LE :: Word24 -> (Word8, Word8, Word8)
unMkWord24LE w =
  let v = fromIntegral w
      (!b0, !b1, !b2, _) = unMkWord32LE v
  in  (b0, b1, b2)

mkWord32LE :: Word8 -> Word8 -> Word8 -> Word8 -> Word32
mkWord32LE b0 b1 b2 b3 =
  (fromIntegral b3 `unsafeShiftL` 24)
    .|. (fromIntegral b2 `unsafeShiftL` 16)
    .|. (fromIntegral b1 `unsafeShiftL` 8)
    .|. fromIntegral b0

unMkWord32LE :: Word32 -> (Word8, Word8, Word8, Word8)
unMkWord32LE w =
  let b0 = fromIntegral w
      b1 = fromIntegral (w `shiftR` 8)
      b2 = fromIntegral (w `shiftR` 16)
      b3 = fromIntegral (w `shiftR` 24)
  in  (b0, b1, b2, b3)

mkWord64LE :: Word8 -> Word8 -> Word8 -> Word8 -> Word8 -> Word8 -> Word8 -> Word8 -> Word64
mkWord64LE b0 b1 b2 b3 b4 b5 b6 b7 =
  (fromIntegral b7 `unsafeShiftL` 56)
    .|. (fromIntegral b6 `unsafeShiftL` 48)
    .|. (fromIntegral b5 `unsafeShiftL` 40)
    .|. (fromIntegral b4 `unsafeShiftL` 32)
    .|. (fromIntegral b3 `unsafeShiftL` 24)
    .|. (fromIntegral b2 `unsafeShiftL` 16)
    .|. (fromIntegral b1 `unsafeShiftL` 8)
    .|. fromIntegral b0

unMkWord64LE :: Word64 -> (Word8, Word8, Word8, Word8, Word8, Word8, Word8, Word8)
unMkWord64LE w =
  let b0 = fromIntegral w
      b1 = fromIntegral (w `shiftR` 8)
      b2 = fromIntegral (w `shiftR` 16)
      b3 = fromIntegral (w `shiftR` 24)
      b4 = fromIntegral (w `shiftR` 32)
      b5 = fromIntegral (w `shiftR` 40)
      b6 = fromIntegral (w `shiftR` 48)
      b7 = fromIntegral (w `shiftR` 56)
  in  (b0, b1, b2, b3, b4, b5, b6, b7)

mkFloatLE :: Word8 -> Word8 -> Word8 -> Word8 -> Float
mkFloatLE b0 b1 b2 b3 = castWord32ToFloat (mkWord32LE b0 b1 b2 b3)

unMkFloatLE :: Float -> (Word8, Word8, Word8, Word8)
unMkFloatLE f = unMkWord32LE (castFloatToWord32 f)

mkDoubleLE :: Word8 -> Word8 -> Word8 -> Word8 -> Word8 -> Word8 -> Word8 -> Word8 -> Double
mkDoubleLE b0 b1 b2 b3 b4 b5 b6 b7 = castWord64ToDouble (mkWord64LE b0 b1 b2 b3 b4 b5 b6 b7)

unMkDoubleLE :: Double -> (Word8, Word8, Word8, Word8, Word8, Word8, Word8, Word8)
unMkDoubleLE f = unMkWord64LE (castDoubleToWord64 f)

class (Num le, Num be) => EndianPair le be | le -> be, be -> le where
  toLittleEndian :: be -> le
  toBigEndian :: le -> be

newtype ViaEndianPair le be = ViaEndianPair {unViaEndianPair :: be}

instance EndianPair Word8 Word8 where
  toLittleEndian = id
  toBigEndian = id

instance EndianPair Int8 Int8 where
  toLittleEndian = id
  toBigEndian = id