packages feed

binrep-0.1.0: src/Binrep/Types/Ints.hs

module Binrep.Types.Ints where

import Binrep.Codec
import Binrep.ByteLen
import GHC.Generics ( Generic )
import Data.Typeable
import Data.Word
import Data.Int
import Data.Aeson
import Data.Serialize

-- | Wrapper type grouping machine integers (sign, size) along with an explicit
--   endianness.
newtype I (sign :: ISign) (size :: ISize) (e :: Endianness)
  = I { getI :: IRep sign size }
    deriving stock (Generic, Typeable)

-- | Lots of deriving boilerplate due to the type family usage.
deriving stock                instance Show     (IRep sign size) => Show     (I sign size e)
deriving via (IRep sign size) instance Eq       (IRep sign size) => Eq       (I sign size e)
deriving via (IRep sign size) instance Ord      (IRep sign size) => Ord      (I sign size e)
deriving via (IRep sign size) instance Bounded  (IRep sign size) => Bounded  (I sign size e)
deriving via (IRep sign size) instance Num      (IRep sign size) => Num      (I sign size e)
deriving via (IRep sign size) instance Real     (IRep sign size) => Real     (I sign size e)
deriving via (IRep sign size) instance Enum     (IRep sign size) => Enum     (I sign size e)
deriving via (IRep sign size) instance Integral (IRep sign size) => Integral (I sign size e)
deriving via (IRep sign size) instance ToJSON   (IRep sign size) => ToJSON   (I sign size e)
deriving via (IRep sign size) instance FromJSON (IRep sign size) => FromJSON (I sign size e)

instance ByteLen (I s 'I1 e) where blen = const 1
instance ByteLen (I s 'I2 e) where blen = const 2
instance ByteLen (I s 'I4 e) where blen = const 4
instance ByteLen (I s 'I8 e) where blen = const 8

-- | Endianness doesn't apply for single-byte machine integers.
instance BinaryCodec (I 'U 'I1 e) where toBin   = putWord8 . getI
                                        fromBin = I <$> getWord8
instance BinaryCodec (I 'S 'I1 e) where toBin   = putInt8 . getI
                                        fromBin = I <$> getInt8

instance BinaryCodec (I 'U 'I2 'BE) where toBin   = putWord16be . getI
                                          fromBin = I <$> getWord16be
instance BinaryCodec (I 'U 'I2 'LE) where toBin   = putWord16le . getI
                                          fromBin = I <$> getWord16le
instance BinaryCodec (I 'S 'I2 'BE) where toBin   = putInt16be . getI
                                          fromBin = I <$> getInt16be
instance BinaryCodec (I 'S 'I2 'LE) where toBin   = putInt16le . getI
                                          fromBin = I <$> getInt16le

instance BinaryCodec (I 'U 'I4 'BE) where toBin   = putWord32be . getI
                                          fromBin = I <$> getWord32be
instance BinaryCodec (I 'U 'I4 'LE) where toBin   = putWord32le . getI
                                          fromBin = I <$> getWord32le
instance BinaryCodec (I 'S 'I4 'BE) where toBin   = putInt32be . getI
                                          fromBin = I <$> getInt32be
instance BinaryCodec (I 'S 'I4 'LE) where toBin   = putInt32le . getI
                                          fromBin = I <$> getInt32le

instance BinaryCodec (I 'U 'I8 'BE) where toBin   = putWord64be . getI
                                          fromBin = I <$> getWord64be
instance BinaryCodec (I 'U 'I8 'LE) where toBin   = putWord64le . getI
                                          fromBin = I <$> getWord64le
instance BinaryCodec (I 'S 'I8 'BE) where toBin   = putInt64be . getI
                                          fromBin = I <$> getInt64be
instance BinaryCodec (I 'S 'I8 'LE) where toBin   = putInt64le . getI
                                          fromBin = I <$> getInt64le

-- | Byte order.
data Endianness
  = BE -- ^ big    endian, MSB first. e.g. most network protocols
  | LE -- ^ little endian, MSB last.  e.g. most processor architectures

-- | Machine integer sign
data ISign
  = S -- ^   signed
  | U -- ^ unsigned

-- | Machine integer size in number of bytes.
data ISize = I1 | I2 | I4 | I8

type family IRep (sign :: ISign) (size :: ISize) where
    IRep 'U 'I1 = Word8
    IRep 'S 'I1 =  Int8
    IRep 'U 'I2 = Word16
    IRep 'S 'I2 =  Int16
    IRep 'U 'I4 = Word32
    IRep 'S 'I4 =  Int32
    IRep 'U 'I8 = Word64
    IRep 'S 'I8 =  Int64