1-- | 2-- Module : Data.Memory.Endian 3-- License : BSD-style 4-- Maintainer : Vincent Hanquez <vincent@snarc.org> 5-- Stability : stable 6-- Portability : good 7-- 8{-# LANGUAGE CPP #-} 9{-# LANGUAGE GeneralizedNewtypeDeriving #-} 10module Data.Memory.Endian 11 ( Endianness(..) 12 , getSystemEndianness 13 , BE(..), LE(..) 14 , fromBE, toBE 15 , fromLE, toLE 16 , ByteSwap 17 ) where 18 19import Data.Word (Word16, Word32, Word64) 20import Foreign.Storable 21#if !defined(ARCH_IS_LITTLE_ENDIAN) && !defined(ARCH_IS_BIG_ENDIAN) 22import Data.Word (Word8) 23import Data.Memory.Internal.Compat (unsafeDoIO) 24import Foreign.Marshal.Alloc 25import Foreign.Ptr 26#endif 27 28import Data.Memory.Internal.Compat (byteSwap64, byteSwap32, byteSwap16) 29 30-- | represent the CPU endianness 31-- 32-- Big endian system stores bytes with the MSB as the first byte. 33-- Little endian system stores bytes with the LSB as the first byte. 34-- 35-- middle endian is purposely avoided. 36data Endianness = LittleEndian 37 | BigEndian 38 deriving (Show,Eq) 39 40-- | Return the system endianness 41getSystemEndianness :: Endianness 42#ifdef ARCH_IS_LITTLE_ENDIAN 43getSystemEndianness = LittleEndian 44#elif ARCH_IS_BIG_ENDIAN 45getSystemEndianness = BigEndian 46#else 47getSystemEndianness 48 | isLittleEndian = LittleEndian 49 | isBigEndian = BigEndian 50 | otherwise = error "cannot determine endianness" 51 where 52 isLittleEndian = endianCheck == 2 53 isBigEndian = endianCheck == 1 54 endianCheck = unsafeDoIO $ alloca $ \p -> do 55 poke p (0x01000002 :: Word32) 56 peek (castPtr p :: Ptr Word8) 57#endif 58 59-- | Little Endian value 60newtype LE a = LE { unLE :: a } 61 deriving (Show,Eq,Storable) 62 63-- | Big Endian value 64newtype BE a = BE { unBE :: a } 65 deriving (Show,Eq,Storable) 66 67-- | Convert a value in cpu endianess to big endian 68toBE :: ByteSwap a => a -> BE a 69#ifdef ARCH_IS_LITTLE_ENDIAN 70toBE = BE . byteSwap 71#elif ARCH_IS_BIG_ENDIAN 72toBE = BE 73#else 74toBE = BE . (if getSystemEndianness == LittleEndian then byteSwap else id) 75#endif 76{-# INLINE toBE #-} 77 78-- | Convert from a big endian value to the cpu endianness 79fromBE :: ByteSwap a => BE a -> a 80#ifdef ARCH_IS_LITTLE_ENDIAN 81fromBE (BE a) = byteSwap a 82#elif ARCH_IS_BIG_ENDIAN 83fromBE (BE a) = a 84#else 85fromBE (BE a) = if getSystemEndianness == LittleEndian then byteSwap a else a 86#endif 87{-# INLINE fromBE #-} 88 89-- | Convert a value in cpu endianess to little endian 90toLE :: ByteSwap a => a -> LE a 91#ifdef ARCH_IS_LITTLE_ENDIAN 92toLE = LE 93#elif ARCH_IS_BIG_ENDIAN 94toLE = LE . byteSwap 95#else 96toLE = LE . (if getSystemEndianness == LittleEndian then id else byteSwap) 97#endif 98{-# INLINE toLE #-} 99 100-- | Convert from a little endian value to the cpu endianness 101fromLE :: ByteSwap a => LE a -> a 102#ifdef ARCH_IS_LITTLE_ENDIAN 103fromLE (LE a) = a 104#elif ARCH_IS_BIG_ENDIAN 105fromLE (LE a) = byteSwap a 106#else 107fromLE (LE a) = if getSystemEndianness == LittleEndian then a else byteSwap a 108#endif 109{-# INLINE fromLE #-} 110 111-- | Class of types that can be byte-swapped. 112-- 113-- e.g. Word16, Word32, Word64 114class Storable a => ByteSwap a where 115 byteSwap :: a -> a 116instance ByteSwap Word16 where 117 byteSwap = byteSwap16 118instance ByteSwap Word32 where 119 byteSwap = byteSwap32 120instance ByteSwap Word64 where 121 byteSwap = byteSwap64 122