byte-order-0.1.3.1: src/System/ByteOrder/Class.hs
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTSyntax #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE RoleAnnotations #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
module System.ByteOrder.Class
( FixedOrdering (..)
, Bytes (..)
) where
import Data.Int (Int16, Int32, Int64, Int8)
import Data.WideWord (Word128 (Word128), Word256 (Word256))
import Data.Word (Word16, Word32, Word64, Word8, byteSwap16, byteSwap32, byteSwap64)
import GHC.ByteOrder (ByteOrder (BigEndian, LittleEndian), targetByteOrder)
import GHC.Word (Word (W#))
import qualified GHC.Exts as Exts
{- | Types that are represented as a fixed-sized word. For these
types, the bytes can be swapped. The instances of this class
use byteswapping primitives and compile-time knowledge of native
endianness to provide portable endianness conversion functions.
-}
class Bytes a where
-- | Convert from a native-endian word to a big-endian word.
toBigEndian :: a -> a
-- | Convert from a native-endian word to a little-endian word.
toLittleEndian :: a -> a
instance Bytes Word8 where
{-# INLINE toBigEndian #-}
{-# INLINE toLittleEndian #-}
toBigEndian = id
toLittleEndian = id
instance Bytes Word16 where
{-# INLINE toBigEndian #-}
{-# INLINE toLittleEndian #-}
toBigEndian = case targetByteOrder of
BigEndian -> id
LittleEndian -> byteSwap16
toLittleEndian = case targetByteOrder of
BigEndian -> byteSwap16
LittleEndian -> id
instance Bytes Word32 where
{-# INLINE toBigEndian #-}
{-# INLINE toLittleEndian #-}
toBigEndian = case targetByteOrder of
BigEndian -> id
LittleEndian -> byteSwap32
toLittleEndian = case targetByteOrder of
BigEndian -> byteSwap32
LittleEndian -> id
instance Bytes Word64 where
{-# INLINE toBigEndian #-}
{-# INLINE toLittleEndian #-}
toBigEndian = case targetByteOrder of
BigEndian -> id
LittleEndian -> byteSwap64
toLittleEndian = case targetByteOrder of
BigEndian -> byteSwap64
LittleEndian -> id
instance Bytes Word128 where
{-# INLINE toBigEndian #-}
{-# INLINE toLittleEndian #-}
toBigEndian = case targetByteOrder of
BigEndian -> id
LittleEndian -> (\(Word128 hi lo) -> Word128 (byteSwap64 lo) (byteSwap64 hi))
toLittleEndian = case targetByteOrder of
BigEndian -> (\(Word128 hi lo) -> Word128 (byteSwap64 lo) (byteSwap64 hi))
LittleEndian -> id
instance Bytes Word256 where
{-# INLINE toBigEndian #-}
{-# INLINE toLittleEndian #-}
toBigEndian = case targetByteOrder of
BigEndian -> id
LittleEndian -> (\(Word256 a b c d) -> Word256 (byteSwap64 d) (byteSwap64 c) (byteSwap64 b) (byteSwap64 a))
toLittleEndian = case targetByteOrder of
BigEndian -> (\(Word256 a b c d) -> Word256 (byteSwap64 d) (byteSwap64 c) (byteSwap64 b) (byteSwap64 a))
LittleEndian -> id
instance Bytes Word where
{-# INLINE toBigEndian #-}
{-# INLINE toLittleEndian #-}
toBigEndian = case targetByteOrder of
BigEndian -> id
LittleEndian -> byteSwap
toLittleEndian = case targetByteOrder of
BigEndian -> byteSwap
LittleEndian -> id
instance Bytes Int8 where
{-# INLINE toBigEndian #-}
{-# INLINE toLittleEndian #-}
toBigEndian = id
toLittleEndian = id
instance Bytes Int16 where
{-# INLINE toBigEndian #-}
{-# INLINE toLittleEndian #-}
toBigEndian = case targetByteOrder of
BigEndian -> id
LittleEndian ->
fromIntegral @Word16 @Int16
. byteSwap16
. fromIntegral @Int16 @Word16
toLittleEndian = case targetByteOrder of
BigEndian ->
fromIntegral @Word16 @Int16
. byteSwap16
. fromIntegral @Int16 @Word16
LittleEndian -> id
instance Bytes Int32 where
{-# INLINE toBigEndian #-}
{-# INLINE toLittleEndian #-}
toBigEndian = case targetByteOrder of
BigEndian -> id
LittleEndian ->
fromIntegral @Word32 @Int32
. byteSwap32
. fromIntegral @Int32 @Word32
toLittleEndian = case targetByteOrder of
BigEndian ->
fromIntegral @Word32 @Int32
. byteSwap32
. fromIntegral @Int32 @Word32
LittleEndian -> id
instance Bytes Int64 where
{-# INLINE toBigEndian #-}
{-# INLINE toLittleEndian #-}
toBigEndian = case targetByteOrder of
BigEndian -> id
LittleEndian ->
fromIntegral @Word64 @Int64
. byteSwap64
. fromIntegral @Int64 @Word64
toLittleEndian = case targetByteOrder of
BigEndian ->
fromIntegral @Word64 @Int64
. byteSwap64
. fromIntegral @Int64 @Word64
LittleEndian -> id
{- | A byte order that can be interpreted as a conversion function.
This class is effectively closed. The only instances are for
'BigEndian' and 'LittleEndian'. It is not possible to write more
instances since there are no other inhabitants of 'ByteOrder'.
-}
class FixedOrdering (b :: ByteOrder) where
toFixedEndian :: (Bytes a) => a -> a
instance FixedOrdering 'LittleEndian where
toFixedEndian = toLittleEndian
instance FixedOrdering 'BigEndian where
toFixedEndian = toBigEndian
byteSwap :: Word -> Word
{-# INLINE byteSwap #-}
byteSwap (W# w) = W# (Exts.byteSwap# w)