packages feed

cairo-image-0.1.0.0: src/System/TargetEndian.hs

{-# LANGUAGE BlockArguments, LambdaCase #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# OPTIONS_GHC -Wall -fno-warn-tabs #-}

module System.TargetEndian (
	-- * To Select According To Endian
	endian ) where

import Language.Haskell.TH (ExpQ, runIO)
import Foreign.Ptr (castPtr)
import Foreign.Marshal (alloca, peekArray)
import Foreign.Storable (poke)
import Data.Word (Word8, Word32)
import System.Environment (lookupEnv)

---------------------------------------------------------------------------

endian :: ExpQ -> ExpQ -> ExpQ
endian el eb = runIO targetEndian >>= \case
	Right LittleEndian -> el; Right BigEndian -> eb
	Right UnknownEndian -> error "Unknown endian"; Left emsg -> error emsg

data Endian = LittleEndian | BigEndian | UnknownEndian deriving Show

targetEndian :: IO (Either String Endian)
targetEndian = lookupEnv "GHC_TARGET_ENDIAN" >>= \case
	Just "little-endian" -> pure $ Right LittleEndian
	Just "big-endian" -> pure $ Right BigEndian
	Just edn -> pure . Left $ "no such endian: " ++ edn ++ "\n" ++
		"\tGHC_TARGET_ENDIAN: little-endian or big-endian"
	Nothing -> Right <$> checkEndian

checkEndian :: IO Endian
checkEndian = (<$> alloca \p -> poke p word32 >> peekArray 4 (castPtr p)) \case
	[4 :: Word8, 3, 2, 1] -> LittleEndian; [1, 2, 3, 4] -> BigEndian
	_ -> UnknownEndian
	where word32 :: Word32; word32 = 0x01020304