{-# language AllowAmbiguousTypes #-} {-# language DataKinds #-} {-# language GADTSyntax #-} {-# language KindSignatures #-} {-# language MagicHash #-} {-# language RoleAnnotations #-} {-# language ScopedTypeVariables #-} {-# language TypeApplications #-} {-# language UnboxedTuples #-} module System.ByteOrder.Class ( FixedOrdering(..) , Bytes(..) ) where import Data.Int (Int8,Int16,Int32,Int64) import Data.Word (Word8,Word16,Word32,Word64) import Data.Word (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 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)