    {-# LANGUAGE CPP, ForeignFunctionInterface #-}
    -- |
    -- Module      : System.Endian
    -- License     : BSD-style
    -- Maintainer  : Vincent Hanquez <vincent@snarc.org>
    -- Stability   : experimental
    -- Portability : unknown
    --
    module System.Endian
        ( Endianness(..)
        , getSystemEndianness
        -- little endian to and from cpu
        , fromLE32
        , fromLE64
        , fromLE16
        , toLE32
        , toLE64
        , toLE16
        -- big endian to and from cpu
        , fromBE32
        , fromBE64
        , fromBE16
        , toBE32
        , toBE64
        , toBE16
        ) where

    #include "MachDeps.h"

    import Foreign.C.Types
    import Data.Word

    -- | represent the CPU endianness
    --
    -- Big endian system stores bytes with the MSB as the first byte.
    -- Little endian system stores bytes with the LSB as the first byte.
    --
    -- middle endian is purposely avoided.
    data Endianness = LittleEndian
                    | BigEndian
                    deriving (Int -> Endianness -> ShowS
    [Endianness] -> ShowS
    Endianness -> String
    (Int -> Endianness -> ShowS)
    -> (Endianness -> String)
    -> ([Endianness] -> ShowS)
    -> Show Endianness
    forall a.
    (Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
    $cshowsPrec :: Int -> Endianness -> ShowS
    showsPrec :: Int -> Endianness -> ShowS
    $cshow :: Endianness -> String
    show :: Endianness -> String
    $cshowList :: [Endianness] -> ShowS
    showList :: [Endianness] -> ShowS
    Show,Endianness -> Endianness -> Bool
    (Endianness -> Endianness -> Bool)
    -> (Endianness -> Endianness -> Bool) -> Eq Endianness
    forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
    $c== :: Endianness -> Endianness -> Bool
    == :: Endianness -> Endianness -> Bool
    $c/= :: Endianness -> Endianness -> Bool
    /= :: Endianness -> Endianness -> Bool
    Eq)

    -- | return the system endianness
    getSystemEndianness :: Endianness
    #ifdef WORDS_BIGENDIAN
    getSystemEndianness = BigEndian
    #else
    getSystemEndianness :: Endianness
    getSystemEndianness = Endianness
    LittleEndian
    #endif

    -- | Convert from a big endian 64 bit value to the cpu endianness
    fromBE64 :: Word64 -> Word64
    fromBE64 :: Word64 -> Word64
    fromBE64 = if Endianness
    getSystemEndianness Endianness -> Endianness -> Bool
    forall a. Eq a => a -> a -> Bool
    == Endianness
    BigEndian then Word64 -> Word64
    forall a. a -> a
    id else Word64 -> Word64
    swap64

    -- | Convert from a little endian 64 bit value to the cpu endianness
    fromLE64 :: Word64 -> Word64
    fromLE64 :: Word64 -> Word64
    fromLE64 = if Endianness
    getSystemEndianness Endianness -> Endianness -> Bool
    forall a. Eq a => a -> a -> Bool
    == Endianness
    LittleEndian then Word64 -> Word64
    forall a. a -> a
    id else Word64 -> Word64
    swap64

    -- | Convert from a big endian 32 bit value to the cpu endianness
    fromBE32 :: Word32 -> Word32
    fromBE32 :: Word32 -> Word32
    fromBE32 = if Endianness
    getSystemEndianness Endianness -> Endianness -> Bool
    forall a. Eq a => a -> a -> Bool
    == Endianness
    BigEndian then Word32 -> Word32
    forall a. a -> a
    id else Word32 -> Word32
    swap32

    -- | Convert from a little endian 32 bit value to the cpu endianness
    fromLE32 :: Word32 -> Word32
    fromLE32 :: Word32 -> Word32
    fromLE32 = if Endianness
    getSystemEndianness Endianness -> Endianness -> Bool
    forall a. Eq a => a -> a -> Bool
    == Endianness
    LittleEndian then Word32 -> Word32
    forall a. a -> a
    id else Word32 -> Word32
    swap32

    -- | Convert from a big endian 16 bit value to the cpu endianness
    fromBE16 :: Word16 -> Word16
    fromBE16 :: Word16 -> Word16
    fromBE16 = if Endianness
    getSystemEndianness Endianness -> Endianness -> Bool
    forall a. Eq a => a -> a -> Bool
    == Endianness
    BigEndian then Word16 -> Word16
    forall a. a -> a
    id else Word16 -> Word16
    swap16

    -- | Convert from a little endian 16 bit value to the cpu endianness
    fromLE16 :: Word16 -> Word16
    fromLE16 :: Word16 -> Word16
    fromLE16 = if Endianness
    getSystemEndianness Endianness -> Endianness -> Bool
    forall a. Eq a => a -> a -> Bool
    == Endianness
    LittleEndian then Word16 -> Word16
    forall a. a -> a
    id else Word16 -> Word16
    swap16

    -- | Convert a 64 bit value in cpu endianess to big endian
    toBE64 :: Word64 -> Word64
    toBE64 :: Word64 -> Word64
    toBE64 = Word64 -> Word64
    fromBE64

    -- | Convert a 64 bit value in cpu endianess to little endian
    toLE64 :: Word64 -> Word64
    toLE64 :: Word64 -> Word64
    toLE64 = Word64 -> Word64
    fromLE64

    -- | Convert a 32 bit value in cpu endianess to big endian
    toBE32 :: Word32 -> Word32
    toBE32 :: Word32 -> Word32
    toBE32 = Word32 -> Word32
    fromBE32

    -- | Convert a 32 bit value in cpu endianess to little endian
    toLE32 :: Word32 -> Word32
    toLE32 :: Word32 -> Word32
    toLE32 = Word32 -> Word32
    fromLE32

    -- | Convert a 16 bit value in cpu endianness to big endian
    toBE16 :: Word16 -> Word16
    toBE16 :: Word16 -> Word16
    toBE16 = Word16 -> Word16
    fromBE16

    -- | Convert a 16 bit value in cpu endianness to little endian
    toLE16 :: Word16 -> Word16
    toLE16 :: Word16 -> Word16
    toLE16 = Word16 -> Word16
    fromLE16

    #if MIN_VERSION_base(4,7,0)

    -- | Transform a 16 bit value bytes from a.b to b.a
    swap16 :: Word16 -> Word16
    swap16 :: Word16 -> Word16
    swap16 = Word16 -> Word16
    byteSwap16

    -- | Transform a 32 bit value bytes from a.b.c.d to d.c.b.a
    swap32 :: Word32 -> Word32
    swap32 :: Word32 -> Word32
    swap32 = Word32 -> Word32
    byteSwap32

    -- | Transform a 64 bit value bytes from a.b.c.d.e.f.g.h to h.g.f.e.d.c.b.a
    swap64 :: Word64 -> Word64
    swap64 :: Word64 -> Word64
    swap64 = Word64 -> Word64
    byteSwap64

    #else

    -- | Transform a 16 bit value bytes from a.b to b.a
    {-# INLINE swap16 #-}
    swap16 :: Word16 -> Word16
    swap16 = fromIntegral . c_swap16 . fromIntegral

    -- | Transform a 32 bit value bytes from a.b.c.d to d.c.b.a
    {-# INLINE swap32 #-}
    swap32 :: Word32 -> Word32
    swap32 = fromIntegral . c_swap32 . fromIntegral

    -- | Transform a 64 bit value bytes from a.b.c.d.e.f.g.h to h.g.f.e.d.c.b.a
    {-# INLINE swap64 #-}
    swap64 :: Word64 -> Word64
    swap64 = fromIntegral . c_swap64 . fromIntegral

    foreign import ccall unsafe "bitfn_swap16" c_swap16 :: CUShort -> CUShort
    foreign import ccall unsafe "bitfn_swap32" c_swap32 :: CUInt -> CUInt
    foreign import ccall unsafe "bitfn_swap64" c_swap64 :: CULLong -> CULLong

    #endif