    {-# LANGUAGE CPP #-}
    {-# LANGUAGE Trustworthy #-}
    -- | Copyright   : (c) 2010-2011 Simon Meier
    -- License       : BSD3-style (see LICENSE)
    --
    -- Maintainer    : Simon Meier <iridcode@gmail.com>
    -- Portability   : GHC
    --
    module Data.ByteString.Builder.Prim.Binary (

      -- ** Binary encodings
        int8
      , word8

      -- *** Big-endian
      , int16BE
      , int32BE
      , int64BE

      , word16BE
      , word32BE
      , word64BE

      , floatBE
      , doubleBE

      -- *** Little-endian
      , int16LE
      , int32LE
      , int64LE

      , word16LE
      , word32LE
      , word64LE

      , floatLE
      , doubleLE

      -- *** Non-portable, host-dependent
      , intHost
      , int16Host
      , int32Host
      , int64Host

      , wordHost
      , word16Host
      , word32Host
      , word64Host

      , floatHost
      , doubleHost

      ) where

    import Data.ByteString.Builder.Prim.Internal
    import Data.ByteString.Builder.Prim.Internal.Floating

    import Foreign

    #include "MachDeps.h"

    ------------------------------------------------------------------------------
    -- Binary encoding
    ------------------------------------------------------------------------------

    -- Word encodings
    -----------------

    -- | Encoding single unsigned bytes as-is.
    --
    {-# INLINE word8 #-}
    word8 :: FixedPrim Word8
    word8 :: FixedPrim Word8
    word8 = FixedPrim Word8
    forall a. Storable a => FixedPrim a
    storableToF

    --
    -- We rely on the fromIntegral to do the right masking for us.
    -- The inlining here is critical, and can be worth 4x performance
    --

    -- | Encoding 'Word16's in big endian format.
    {-# INLINE word16BE #-}
    word16BE :: FixedPrim Word16
    #ifdef WORDS_BIGENDIAN
    word16BE = word16Host
    #else
    word16BE :: FixedPrim Word16
    word16BE = Int -> (Word16 -> Ptr Word8 -> IO ()) -> FixedPrim Word16
    forall a. Int -> (a -> Ptr Word8 -> IO ()) -> FixedPrim a
    fixedPrim Int
    2 ((Word16 -> Ptr Word8 -> IO ()) -> FixedPrim Word16)
    -> (Word16 -> Ptr Word8 -> IO ()) -> FixedPrim Word16
    forall a b. (a -> b) -> a -> b
    $ \Word16
    w Ptr Word8
    p -> do
        Ptr Word8 -> Word8 -> IO ()
    forall a. Storable a => Ptr a -> a -> IO ()
    poke Ptr Word8
    p               (Word16 -> Word8
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral (Word16 -> Int -> Word16
    forall a. Bits a => a -> Int -> a
    shiftR Word16
    w Int
    8) :: Word8)
        Ptr Word8 -> Word8 -> IO ()
    forall a. Storable a => Ptr a -> a -> IO ()
    poke (Ptr Word8
    p Ptr Word8 -> Int -> Ptr Word8
    forall a b. Ptr a -> Int -> Ptr b
    `plusPtr` Int
    1) (Word16 -> Word8
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Word16
    w                :: Word8)
    #endif

    -- | Encoding 'Word16's in little endian format.
    {-# INLINE word16LE #-}
    word16LE :: FixedPrim Word16
    #ifdef WORDS_BIGENDIAN
    word16LE = fixedPrim 2 $ \w p -> do
        poke p               (fromIntegral w                :: Word8)
        poke (p `plusPtr` 1) (fromIntegral (shiftR w 8) :: Word8)
    #else
    word16LE :: FixedPrim Word16
    word16LE = FixedPrim Word16
    word16Host
    #endif

    -- | Encoding 'Word32's in big endian format.
    {-# INLINE word32BE #-}
    word32BE :: FixedPrim Word32
    #ifdef WORDS_BIGENDIAN
    word32BE = word32Host
    #else
    word32BE :: FixedPrim Word32
    word32BE = Int -> (Word32 -> Ptr Word8 -> IO ()) -> FixedPrim Word32
    forall a. Int -> (a -> Ptr Word8 -> IO ()) -> FixedPrim a
    fixedPrim Int
    4 ((Word32 -> Ptr Word8 -> IO ()) -> FixedPrim Word32)
    -> (Word32 -> Ptr Word8 -> IO ()) -> FixedPrim Word32
    forall a b. (a -> b) -> a -> b
    $ \Word32
    w Ptr Word8
    p -> do
        Ptr Word8 -> Word8 -> IO ()
    forall a. Storable a => Ptr a -> a -> IO ()
    poke Ptr Word8
    p               (Word32 -> Word8
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral (Word32 -> Int -> Word32
    forall a. Bits a => a -> Int -> a
    shiftR Word32
    w Int
    24) :: Word8)
        Ptr Word8 -> Word8 -> IO ()
    forall a. Storable a => Ptr a -> a -> IO ()
    poke (Ptr Word8
    p Ptr Word8 -> Int -> Ptr Word8
    forall a b. Ptr a -> Int -> Ptr b
    `plusPtr` Int
    1) (Word32 -> Word8
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral (Word32 -> Int -> Word32
    forall a. Bits a => a -> Int -> a
    shiftR Word32
    w Int
    16) :: Word8)
        Ptr Word8 -> Word8 -> IO ()
    forall a. Storable a => Ptr a -> a -> IO ()
    poke (Ptr Word8
    p Ptr Word8 -> Int -> Ptr Word8
    forall a b. Ptr a -> Int -> Ptr b
    `plusPtr` Int
    2) (Word32 -> Word8
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral (Word32 -> Int -> Word32
    forall a. Bits a => a -> Int -> a
    shiftR Word32
    w  Int
    8) :: Word8)
        Ptr Word8 -> Word8 -> IO ()
    forall a. Storable a => Ptr a -> a -> IO ()
    poke (Ptr Word8
    p Ptr Word8 -> Int -> Ptr Word8
    forall a b. Ptr a -> Int -> Ptr b
    `plusPtr` Int
    3) (Word32 -> Word8
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Word32
    w                 :: Word8)
    #endif

    -- | Encoding 'Word32's in little endian format.
    {-# INLINE word32LE #-}
    word32LE :: FixedPrim Word32
    #ifdef WORDS_BIGENDIAN
    word32LE = fixedPrim 4 $ \w p -> do
        poke p               (fromIntegral w                 :: Word8)
        poke (p `plusPtr` 1) (fromIntegral (shiftR w  8) :: Word8)
        poke (p `plusPtr` 2) (fromIntegral (shiftR w 16) :: Word8)
        poke (p `plusPtr` 3) (fromIntegral (shiftR w 24) :: Word8)
    #else
    word32LE :: FixedPrim Word32
    word32LE = FixedPrim Word32
    word32Host
    #endif

    -- on a little endian machine:
    -- word32LE w32 = fixedPrim 4 (\w p -> poke (castPtr p) w32)

    -- | Encoding 'Word64's in big endian format.
    {-# INLINE word64BE #-}
    word64BE :: FixedPrim Word64
    #ifdef WORDS_BIGENDIAN
    word64BE = word64Host
    #else
    #if WORD_SIZE_IN_BITS < 64
    --
    -- To avoid expensive 64 bit shifts on 32 bit machines, we cast to
    -- Word32, and write that
    --
    word64BE =
        fixedPrim 8 $ \w p -> do
            let a = fromIntegral (shiftR w 32) :: Word32
                b = fromIntegral w                 :: Word32
            poke p               (fromIntegral (shiftR a 24) :: Word8)
            poke (p `plusPtr` 1) (fromIntegral (shiftR a 16) :: Word8)
            poke (p `plusPtr` 2) (fromIntegral (shiftR a  8) :: Word8)
            poke (p `plusPtr` 3) (fromIntegral a                 :: Word8)
            poke (p `plusPtr` 4) (fromIntegral (shiftR b 24) :: Word8)
            poke (p `plusPtr` 5) (fromIntegral (shiftR b 16) :: Word8)
            poke (p `plusPtr` 6) (fromIntegral (shiftR b  8) :: Word8)
            poke (p `plusPtr` 7) (fromIntegral b                 :: Word8)
    #else
    word64BE :: FixedPrim Word64
    word64BE = Int -> (Word64 -> Ptr Word8 -> IO ()) -> FixedPrim Word64
    forall a. Int -> (a -> Ptr Word8 -> IO ()) -> FixedPrim a
    fixedPrim Int
    8 ((Word64 -> Ptr Word8 -> IO ()) -> FixedPrim Word64)
    -> (Word64 -> Ptr Word8 -> IO ()) -> FixedPrim Word64
    forall a b. (a -> b) -> a -> b
    $ \Word64
    w Ptr Word8
    p -> do
        Ptr Word8 -> Word8 -> IO ()
    forall a. Storable a => Ptr a -> a -> IO ()
    poke Ptr Word8
    p               (Word64 -> Word8
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral (Word64 -> Int -> Word64
    forall a. Bits a => a -> Int -> a
    shiftR Word64
    w Int
    56) :: Word8)
        Ptr Word8 -> Word8 -> IO ()
    forall a. Storable a => Ptr a -> a -> IO ()
    poke (Ptr Word8
    p Ptr Word8 -> Int -> Ptr Word8
    forall a b. Ptr a -> Int -> Ptr b
    `plusPtr` Int
    1) (Word64 -> Word8
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral (Word64 -> Int -> Word64
    forall a. Bits a => a -> Int -> a
    shiftR Word64
    w Int
    48) :: Word8)
        Ptr Word8 -> Word8 -> IO ()
    forall a. Storable a => Ptr a -> a -> IO ()
    poke (Ptr Word8
    p Ptr Word8 -> Int -> Ptr Word8
    forall a b. Ptr a -> Int -> Ptr b
    `plusPtr` Int
    2) (Word64 -> Word8
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral (Word64 -> Int -> Word64
    forall a. Bits a => a -> Int -> a
    shiftR Word64
    w Int
    40) :: Word8)
        Ptr Word8 -> Word8 -> IO ()
    forall a. Storable a => Ptr a -> a -> IO ()
    poke (Ptr Word8
    p Ptr Word8 -> Int -> Ptr Word8
    forall a b. Ptr a -> Int -> Ptr b
    `plusPtr` Int
    3) (Word64 -> Word8
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral (Word64 -> Int -> Word64
    forall a. Bits a => a -> Int -> a
    shiftR Word64
    w Int
    32) :: Word8)
        Ptr Word8 -> Word8 -> IO ()
    forall a. Storable a => Ptr a -> a -> IO ()
    poke (Ptr Word8
    p Ptr Word8 -> Int -> Ptr Word8
    forall a b. Ptr a -> Int -> Ptr b
    `plusPtr` Int
    4) (Word64 -> Word8
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral (Word64 -> Int -> Word64
    forall a. Bits a => a -> Int -> a
    shiftR Word64
    w Int
    24) :: Word8)
        Ptr Word8 -> Word8 -> IO ()
    forall a. Storable a => Ptr a -> a -> IO ()
    poke (Ptr Word8
    p Ptr Word8 -> Int -> Ptr Word8
    forall a b. Ptr a -> Int -> Ptr b
    `plusPtr` Int
    5) (Word64 -> Word8
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral (Word64 -> Int -> Word64
    forall a. Bits a => a -> Int -> a
    shiftR Word64
    w Int
    16) :: Word8)
        Ptr Word8 -> Word8 -> IO ()
    forall a. Storable a => Ptr a -> a -> IO ()
    poke (Ptr Word8
    p Ptr Word8 -> Int -> Ptr Word8
    forall a b. Ptr a -> Int -> Ptr b
    `plusPtr` Int
    6) (Word64 -> Word8
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral (Word64 -> Int -> Word64
    forall a. Bits a => a -> Int -> a
    shiftR Word64
    w  Int
    8) :: Word8)
        Ptr Word8 -> Word8 -> IO ()
    forall a. Storable a => Ptr a -> a -> IO ()
    poke (Ptr Word8
    p Ptr Word8 -> Int -> Ptr Word8
    forall a b. Ptr a -> Int -> Ptr b
    `plusPtr` Int
    7) (Word64 -> Word8
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Word64
    w                 :: Word8)
    #endif
    #endif

    -- | Encoding 'Word64's in little endian format.
    {-# INLINE word64LE #-}
    word64LE :: FixedPrim Word64
    #ifdef WORDS_BIGENDIAN
    #if WORD_SIZE_IN_BITS < 64
    word64LE =
        fixedPrim 8 $ \w p -> do
            let b = fromIntegral (shiftR w 32) :: Word32
                a = fromIntegral w                 :: Word32
            poke (p)             (fromIntegral a                 :: Word8)
            poke (p `plusPtr` 1) (fromIntegral (shiftR a  8) :: Word8)
            poke (p `plusPtr` 2) (fromIntegral (shiftR a 16) :: Word8)
            poke (p `plusPtr` 3) (fromIntegral (shiftR a 24) :: Word8)
            poke (p `plusPtr` 4) (fromIntegral b                 :: Word8)
            poke (p `plusPtr` 5) (fromIntegral (shiftR b  8) :: Word8)
            poke (p `plusPtr` 6) (fromIntegral (shiftR b 16) :: Word8)
            poke (p `plusPtr` 7) (fromIntegral (shiftR b 24) :: Word8)
    #else
    word64LE = fixedPrim 8 $ \w p -> do
        poke p               (fromIntegral w                 :: Word8)
        poke (p `plusPtr` 1) (fromIntegral (shiftR w  8) :: Word8)
        poke (p `plusPtr` 2) (fromIntegral (shiftR w 16) :: Word8)
        poke (p `plusPtr` 3) (fromIntegral (shiftR w 24) :: Word8)
        poke (p `plusPtr` 4) (fromIntegral (shiftR w 32) :: Word8)
        poke (p `plusPtr` 5) (fromIntegral (shiftR w 40) :: Word8)
        poke (p `plusPtr` 6) (fromIntegral (shiftR w 48) :: Word8)
        poke (p `plusPtr` 7) (fromIntegral (shiftR w 56) :: Word8)
    #endif
    #else
    word64LE :: FixedPrim Word64
    word64LE = FixedPrim Word64
    word64Host
    #endif

    -- | Encode a single native machine 'Word'. The 'Word's is encoded in host order,
    -- host endian form, for the machine you are on. On a 64 bit machine the 'Word'
    -- is an 8 byte value, on a 32 bit machine, 4 bytes. Values encoded this way
    -- are not portable to different endian or word sized machines, without
    -- conversion.
    --
    {-# INLINE wordHost #-}
    wordHost :: FixedPrim Word
    wordHost :: FixedPrim Word
    wordHost = FixedPrim Word
    forall a. Storable a => FixedPrim a
    storableToF

    -- | Encoding 'Word16's in native host order and host endianness.
    {-# INLINE word16Host #-}
    word16Host :: FixedPrim Word16
    word16Host :: FixedPrim Word16
    word16Host = FixedPrim Word16
    forall a. Storable a => FixedPrim a
    storableToF

    -- | Encoding 'Word32's in native host order and host endianness.
    {-# INLINE word32Host #-}
    word32Host :: FixedPrim Word32
    word32Host :: FixedPrim Word32
    word32Host = FixedPrim Word32
    forall a. Storable a => FixedPrim a
    storableToF

    -- | Encoding 'Word64's in native host order and host endianness.
    {-# INLINE word64Host #-}
    word64Host :: FixedPrim Word64
    word64Host :: FixedPrim Word64
    word64Host = FixedPrim Word64
    forall a. Storable a => FixedPrim a
    storableToF

    ------------------------------------------------------------------------------
    -- Int encodings
    ------------------------------------------------------------------------------
    --
    -- We rely on 'fromIntegral' to do a loss-less conversion to the corresponding
    -- 'Word' type
    --
    ------------------------------------------------------------------------------

    -- | Encoding single signed bytes as-is.
    --
    {-# INLINE int8 #-}
    int8 :: FixedPrim Int8
    int8 :: FixedPrim Int8
    int8 = Int8 -> Word8
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral (Int8 -> Word8) -> FixedPrim Word8 -> FixedPrim Int8
    forall (f :: * -> *) b a. Contravariant f => (b -> a) -> f a -> f b
    >$< FixedPrim Word8
    word8

    -- | Encoding 'Int16's in big endian format.
    {-# INLINE int16BE #-}
    int16BE :: FixedPrim Int16
    int16BE :: FixedPrim Int16
    int16BE = Int16 -> Word16
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral (Int16 -> Word16) -> FixedPrim Word16 -> FixedPrim Int16
    forall (f :: * -> *) b a. Contravariant f => (b -> a) -> f a -> f b
    >$< FixedPrim Word16
    word16BE

    -- | Encoding 'Int16's in little endian format.
    {-# INLINE int16LE #-}
    int16LE :: FixedPrim Int16
    int16LE :: FixedPrim Int16
    int16LE = Int16 -> Word16
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral (Int16 -> Word16) -> FixedPrim Word16 -> FixedPrim Int16
    forall (f :: * -> *) b a. Contravariant f => (b -> a) -> f a -> f b
    >$< FixedPrim Word16
    word16LE

    -- | Encoding 'Int32's in big endian format.
    {-# INLINE int32BE #-}
    int32BE :: FixedPrim Int32
    int32BE :: FixedPrim Int32
    int32BE = Int32 -> Word32
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral (Int32 -> Word32) -> FixedPrim Word32 -> FixedPrim Int32
    forall (f :: * -> *) b a. Contravariant f => (b -> a) -> f a -> f b
    >$< FixedPrim Word32
    word32BE

    -- | Encoding 'Int32's in little endian format.
    {-# INLINE int32LE #-}
    int32LE :: FixedPrim Int32
    int32LE :: FixedPrim Int32
    int32LE = Int32 -> Word32
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral (Int32 -> Word32) -> FixedPrim Word32 -> FixedPrim Int32
    forall (f :: * -> *) b a. Contravariant f => (b -> a) -> f a -> f b
    >$< FixedPrim Word32
    word32LE

    -- | Encoding 'Int64's in big endian format.
    {-# INLINE int64BE #-}
    int64BE :: FixedPrim Int64
    int64BE :: FixedPrim Int64
    int64BE = Int64 -> Word64
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral (Int64 -> Word64) -> FixedPrim Word64 -> FixedPrim Int64
    forall (f :: * -> *) b a. Contravariant f => (b -> a) -> f a -> f b
    >$< FixedPrim Word64
    word64BE

    -- | Encoding 'Int64's in little endian format.
    {-# INLINE int64LE #-}
    int64LE :: FixedPrim Int64
    int64LE :: FixedPrim Int64
    int64LE = Int64 -> Word64
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral (Int64 -> Word64) -> FixedPrim Word64 -> FixedPrim Int64
    forall (f :: * -> *) b a. Contravariant f => (b -> a) -> f a -> f b
    >$< FixedPrim Word64
    word64LE

    -- | Encode a single native machine 'Int'. The 'Int's is encoded in host order,
    -- host endian form, for the machine you are on. On a 64 bit machine the 'Int'
    -- is an 8 byte value, on a 32 bit machine, 4 bytes. Values encoded this way
    -- are not portable to different endian or integer sized machines, without
    -- conversion.
    --
    {-# INLINE intHost #-}
    intHost :: FixedPrim Int
    intHost :: FixedPrim Int
    intHost = FixedPrim Int
    forall a. Storable a => FixedPrim a
    storableToF

    -- | Encoding 'Int16's in native host order and host endianness.
    {-# INLINE int16Host #-}
    int16Host :: FixedPrim Int16
    int16Host :: FixedPrim Int16
    int16Host = FixedPrim Int16
    forall a. Storable a => FixedPrim a
    storableToF

    -- | Encoding 'Int32's in native host order and host endianness.
    {-# INLINE int32Host #-}
    int32Host :: FixedPrim Int32
    int32Host :: FixedPrim Int32
    int32Host = FixedPrim Int32
    forall a. Storable a => FixedPrim a
    storableToF

    -- | Encoding 'Int64's in native host order and host endianness.
    {-# INLINE int64Host #-}
    int64Host :: FixedPrim Int64
    int64Host :: FixedPrim Int64
    int64Host = FixedPrim Int64
    forall a. Storable a => FixedPrim a
    storableToF

    -- IEEE Floating Point Numbers
    ------------------------------

    -- | Encode a 'Float' in big endian format.
    {-# INLINE floatBE #-}
    floatBE :: FixedPrim Float
    floatBE :: FixedPrim Float
    floatBE = FixedPrim Word32 -> FixedPrim Float
    encodeFloatViaWord32F FixedPrim Word32
    word32BE

    -- | Encode a 'Float' in little endian format.
    {-# INLINE floatLE #-}
    floatLE :: FixedPrim Float
    floatLE :: FixedPrim Float
    floatLE = FixedPrim Word32 -> FixedPrim Float
    encodeFloatViaWord32F FixedPrim Word32
    word32LE

    -- | Encode a 'Double' in big endian format.
    {-# INLINE doubleBE #-}
    doubleBE :: FixedPrim Double
    doubleBE :: FixedPrim Double
    doubleBE = FixedPrim Word64 -> FixedPrim Double
    encodeDoubleViaWord64F FixedPrim Word64
    word64BE

    -- | Encode a 'Double' in little endian format.
    {-# INLINE doubleLE #-}
    doubleLE :: FixedPrim Double
    doubleLE :: FixedPrim Double
    doubleLE = FixedPrim Word64 -> FixedPrim Double
    encodeDoubleViaWord64F FixedPrim Word64
    word64LE

    -- | Encode a 'Float' in native host order and host endianness. Values written
    -- this way are not portable to different endian machines, without conversion.
    --
    {-# INLINE floatHost #-}
    floatHost :: FixedPrim Float
    floatHost :: FixedPrim Float
    floatHost = FixedPrim Float
    forall a. Storable a => FixedPrim a
    storableToF

    -- | Encode a 'Double' in native host order and host endianness.
    {-# INLINE doubleHost #-}
    doubleHost :: FixedPrim Double
    doubleHost :: FixedPrim Double
    doubleHost = FixedPrim Double
    forall a. Storable a => FixedPrim a
    storableToF