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