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 { forall (sign :: ISign) (size :: ISize) (e :: Endianness).
I sign size e -> IRep sign size
getI :: IRep sign size }
    deriving stock ((forall x. I sign size e -> Rep (I sign size e) x)
-> (forall x. Rep (I sign size e) x -> I sign size e)
-> Generic (I sign size e)
forall x. Rep (I sign size e) x -> I sign size e
forall x. I sign size e -> Rep (I sign size e) x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
forall (sign :: ISign) (size :: ISize) (e :: Endianness) x.
Rep (I sign size e) x -> I sign size e
forall (sign :: ISign) (size :: ISize) (e :: Endianness) x.
I sign size e -> Rep (I sign size e) x
$cto :: forall (sign :: ISign) (size :: ISize) (e :: Endianness) x.
Rep (I sign size e) x -> I sign size e
$cfrom :: forall (sign :: ISign) (size :: ISize) (e :: Endianness) x.
I sign size e -> Rep (I sign size e) x
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 :: I s 'I1 e -> Natural
blen = Natural -> I s 'I1 e -> Natural
forall a b. a -> b -> a
const Natural
1
instance ByteLen (I s 'I2 e) where blen :: I s 'I2 e -> Natural
blen = Natural -> I s 'I2 e -> Natural
forall a b. a -> b -> a
const Natural
2
instance ByteLen (I s 'I4 e) where blen :: I s 'I4 e -> Natural
blen = Natural -> I s 'I4 e -> Natural
forall a b. a -> b -> a
const Natural
4
instance ByteLen (I s 'I8 e) where blen :: I s 'I8 e -> Natural
blen = Natural -> I s 'I8 e -> Natural
forall a b. a -> b -> a
const Natural
8

-- | Endianness doesn't apply for single-byte machine integers.
instance BinaryCodec (I 'U 'I1 e) where toBin :: Putter (I 'U 'I1 e)
toBin   = Putter Word8
putWord8 Putter Word8 -> (I 'U 'I1 e -> Word8) -> Putter (I 'U 'I1 e)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. I 'U 'I1 e -> Word8
forall (sign :: ISign) (size :: ISize) (e :: Endianness).
I sign size e -> IRep sign size
getI
                                        fromBin :: Get (I 'U 'I1 e)
fromBin = Word8 -> I 'U 'I1 e
forall (sign :: ISign) (size :: ISize) (e :: Endianness).
IRep sign size -> I sign size e
I (Word8 -> I 'U 'I1 e) -> Get Word8 -> Get (I 'U 'I1 e)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Get Word8
getWord8
instance BinaryCodec (I 'S 'I1 e) where toBin :: Putter (I 'S 'I1 e)
toBin   = Putter Int8
putInt8 Putter Int8 -> (I 'S 'I1 e -> Int8) -> Putter (I 'S 'I1 e)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. I 'S 'I1 e -> Int8
forall (sign :: ISign) (size :: ISize) (e :: Endianness).
I sign size e -> IRep sign size
getI
                                        fromBin :: Get (I 'S 'I1 e)
fromBin = Int8 -> I 'S 'I1 e
forall (sign :: ISign) (size :: ISize) (e :: Endianness).
IRep sign size -> I sign size e
I (Int8 -> I 'S 'I1 e) -> Get Int8 -> Get (I 'S 'I1 e)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Get Int8
getInt8

instance BinaryCodec (I 'U 'I2 'BE) where toBin :: Putter (I 'U 'I2 'BE)
toBin   = Putter Word16
putWord16be Putter Word16 -> (I 'U 'I2 'BE -> Word16) -> Putter (I 'U 'I2 'BE)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. I 'U 'I2 'BE -> Word16
forall (sign :: ISign) (size :: ISize) (e :: Endianness).
I sign size e -> IRep sign size
getI
                                          fromBin :: Get (I 'U 'I2 'BE)
fromBin = Word16 -> I 'U 'I2 'BE
forall (sign :: ISign) (size :: ISize) (e :: Endianness).
IRep sign size -> I sign size e
I (Word16 -> I 'U 'I2 'BE) -> Get Word16 -> Get (I 'U 'I2 'BE)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Get Word16
getWord16be
instance BinaryCodec (I 'U 'I2 'LE) where toBin :: Putter (I 'U 'I2 'LE)
toBin   = Putter Word16
putWord16le Putter Word16 -> (I 'U 'I2 'LE -> Word16) -> Putter (I 'U 'I2 'LE)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. I 'U 'I2 'LE -> Word16
forall (sign :: ISign) (size :: ISize) (e :: Endianness).
I sign size e -> IRep sign size
getI
                                          fromBin :: Get (I 'U 'I2 'LE)
fromBin = Word16 -> I 'U 'I2 'LE
forall (sign :: ISign) (size :: ISize) (e :: Endianness).
IRep sign size -> I sign size e
I (Word16 -> I 'U 'I2 'LE) -> Get Word16 -> Get (I 'U 'I2 'LE)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Get Word16
getWord16le
instance BinaryCodec (I 'S 'I2 'BE) where toBin :: Putter (I 'S 'I2 'BE)
toBin   = Putter Int16
putInt16be Putter Int16 -> (I 'S 'I2 'BE -> Int16) -> Putter (I 'S 'I2 'BE)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. I 'S 'I2 'BE -> Int16
forall (sign :: ISign) (size :: ISize) (e :: Endianness).
I sign size e -> IRep sign size
getI
                                          fromBin :: Get (I 'S 'I2 'BE)
fromBin = Int16 -> I 'S 'I2 'BE
forall (sign :: ISign) (size :: ISize) (e :: Endianness).
IRep sign size -> I sign size e
I (Int16 -> I 'S 'I2 'BE) -> Get Int16 -> Get (I 'S 'I2 'BE)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Get Int16
getInt16be
instance BinaryCodec (I 'S 'I2 'LE) where toBin :: Putter (I 'S 'I2 'LE)
toBin   = Putter Int16
putInt16le Putter Int16 -> (I 'S 'I2 'LE -> Int16) -> Putter (I 'S 'I2 'LE)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. I 'S 'I2 'LE -> Int16
forall (sign :: ISign) (size :: ISize) (e :: Endianness).
I sign size e -> IRep sign size
getI
                                          fromBin :: Get (I 'S 'I2 'LE)
fromBin = Int16 -> I 'S 'I2 'LE
forall (sign :: ISign) (size :: ISize) (e :: Endianness).
IRep sign size -> I sign size e
I (Int16 -> I 'S 'I2 'LE) -> Get Int16 -> Get (I 'S 'I2 'LE)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Get Int16
getInt16le

instance BinaryCodec (I 'U 'I4 'BE) where toBin :: Putter (I 'U 'I4 'BE)
toBin   = Putter Word32
putWord32be Putter Word32 -> (I 'U 'I4 'BE -> Word32) -> Putter (I 'U 'I4 'BE)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. I 'U 'I4 'BE -> Word32
forall (sign :: ISign) (size :: ISize) (e :: Endianness).
I sign size e -> IRep sign size
getI
                                          fromBin :: Get (I 'U 'I4 'BE)
fromBin = Word32 -> I 'U 'I4 'BE
forall (sign :: ISign) (size :: ISize) (e :: Endianness).
IRep sign size -> I sign size e
I (Word32 -> I 'U 'I4 'BE) -> Get Word32 -> Get (I 'U 'I4 'BE)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Get Word32
getWord32be
instance BinaryCodec (I 'U 'I4 'LE) where toBin :: Putter (I 'U 'I4 'LE)
toBin   = Putter Word32
putWord32le Putter Word32 -> (I 'U 'I4 'LE -> Word32) -> Putter (I 'U 'I4 'LE)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. I 'U 'I4 'LE -> Word32
forall (sign :: ISign) (size :: ISize) (e :: Endianness).
I sign size e -> IRep sign size
getI
                                          fromBin :: Get (I 'U 'I4 'LE)
fromBin = Word32 -> I 'U 'I4 'LE
forall (sign :: ISign) (size :: ISize) (e :: Endianness).
IRep sign size -> I sign size e
I (Word32 -> I 'U 'I4 'LE) -> Get Word32 -> Get (I 'U 'I4 'LE)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Get Word32
getWord32le
instance BinaryCodec (I 'S 'I4 'BE) where toBin :: Putter (I 'S 'I4 'BE)
toBin   = Putter Int32
putInt32be Putter Int32 -> (I 'S 'I4 'BE -> Int32) -> Putter (I 'S 'I4 'BE)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. I 'S 'I4 'BE -> Int32
forall (sign :: ISign) (size :: ISize) (e :: Endianness).
I sign size e -> IRep sign size
getI
                                          fromBin :: Get (I 'S 'I4 'BE)
fromBin = Int32 -> I 'S 'I4 'BE
forall (sign :: ISign) (size :: ISize) (e :: Endianness).
IRep sign size -> I sign size e
I (Int32 -> I 'S 'I4 'BE) -> Get Int32 -> Get (I 'S 'I4 'BE)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Get Int32
getInt32be
instance BinaryCodec (I 'S 'I4 'LE) where toBin :: Putter (I 'S 'I4 'LE)
toBin   = Putter Int32
putInt32le Putter Int32 -> (I 'S 'I4 'LE -> Int32) -> Putter (I 'S 'I4 'LE)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. I 'S 'I4 'LE -> Int32
forall (sign :: ISign) (size :: ISize) (e :: Endianness).
I sign size e -> IRep sign size
getI
                                          fromBin :: Get (I 'S 'I4 'LE)
fromBin = Int32 -> I 'S 'I4 'LE
forall (sign :: ISign) (size :: ISize) (e :: Endianness).
IRep sign size -> I sign size e
I (Int32 -> I 'S 'I4 'LE) -> Get Int32 -> Get (I 'S 'I4 'LE)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Get Int32
getInt32le

instance BinaryCodec (I 'U 'I8 'BE) where toBin :: Putter (I 'U 'I8 'BE)
toBin   = Putter Word64
putWord64be Putter Word64 -> (I 'U 'I8 'BE -> Word64) -> Putter (I 'U 'I8 'BE)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. I 'U 'I8 'BE -> Word64
forall (sign :: ISign) (size :: ISize) (e :: Endianness).
I sign size e -> IRep sign size
getI
                                          fromBin :: Get (I 'U 'I8 'BE)
fromBin = Word64 -> I 'U 'I8 'BE
forall (sign :: ISign) (size :: ISize) (e :: Endianness).
IRep sign size -> I sign size e
I (Word64 -> I 'U 'I8 'BE) -> Get Word64 -> Get (I 'U 'I8 'BE)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Get Word64
getWord64be
instance BinaryCodec (I 'U 'I8 'LE) where toBin :: Putter (I 'U 'I8 'LE)
toBin   = Putter Word64
putWord64le Putter Word64 -> (I 'U 'I8 'LE -> Word64) -> Putter (I 'U 'I8 'LE)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. I 'U 'I8 'LE -> Word64
forall (sign :: ISign) (size :: ISize) (e :: Endianness).
I sign size e -> IRep sign size
getI
                                          fromBin :: Get (I 'U 'I8 'LE)
fromBin = Word64 -> I 'U 'I8 'LE
forall (sign :: ISign) (size :: ISize) (e :: Endianness).
IRep sign size -> I sign size e
I (Word64 -> I 'U 'I8 'LE) -> Get Word64 -> Get (I 'U 'I8 'LE)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Get Word64
getWord64le
instance BinaryCodec (I 'S 'I8 'BE) where toBin :: Putter (I 'S 'I8 'BE)
toBin   = Putter Int64
putInt64be Putter Int64 -> (I 'S 'I8 'BE -> Int64) -> Putter (I 'S 'I8 'BE)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. I 'S 'I8 'BE -> Int64
forall (sign :: ISign) (size :: ISize) (e :: Endianness).
I sign size e -> IRep sign size
getI
                                          fromBin :: Get (I 'S 'I8 'BE)
fromBin = Int64 -> I 'S 'I8 'BE
forall (sign :: ISign) (size :: ISize) (e :: Endianness).
IRep sign size -> I sign size e
I (Int64 -> I 'S 'I8 'BE) -> Get Int64 -> Get (I 'S 'I8 'BE)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Get Int64
getInt64be
instance BinaryCodec (I 'S 'I8 'LE) where toBin :: Putter (I 'S 'I8 'LE)
toBin   = Putter Int64
putInt64le Putter Int64 -> (I 'S 'I8 'LE -> Int64) -> Putter (I 'S 'I8 'LE)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. I 'S 'I8 'LE -> Int64
forall (sign :: ISign) (size :: ISize) (e :: Endianness).
I sign size e -> IRep sign size
getI
                                          fromBin :: Get (I 'S 'I8 'LE)
fromBin = Int64 -> I 'S 'I8 'LE
forall (sign :: ISign) (size :: ISize) (e :: Endianness).
IRep sign size -> I sign size e
I (Int64 -> I 'S 'I8 'LE) -> Get Int64 -> Get (I 'S 'I8 'LE)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Get Int64
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